2
0
Эх сурвалжийг харах

Adds OpenStack provider token auth

Alessandro Pilotti 10 жил өмнө
parent
commit
c450b78c9d

+ 5 - 2
coriolis/api/auth.py

@@ -30,6 +30,8 @@ class CoriolisKeystoneContext(wsgi.Middleware):
             tenant = req.headers['X_TENANT']
             tenant = req.headers['X_TENANT']
 
 
         project_name = req.headers.get('X_TENANT_NAME')
         project_name = req.headers.get('X_TENANT_NAME')
+        project_domain_name = req.headers.get('X-Project-Domain-Name')
+        user_domain_name = req.headers.get('X-User-Domain-Name')
 
 
         req_id = req.environ.get(request_id.ENV_REQUEST_ID)
         req_id = req.environ.get(request_id.ENV_REQUEST_ID)
         # TODO(alexpilotti): Check why it's not str
         # TODO(alexpilotti): Check why it's not str
@@ -37,8 +39,7 @@ class CoriolisKeystoneContext(wsgi.Middleware):
             req_id = req_id.decode()
             req_id = req_id.decode()
 
 
         # Get the auth token
         # Get the auth token
-        auth_token = req.headers.get('X_AUTH_TOKEN',
-                                     req.headers.get('X_STORAGE_TOKEN'))
+        auth_token = req.headers.get('X_AUTH_TOKEN')
 
 
         # Build a context, including the auth_token...
         # Build a context, including the auth_token...
         remote_address = req.remote_addr
         remote_address = req.remote_addr
@@ -55,6 +56,8 @@ class CoriolisKeystoneContext(wsgi.Middleware):
         ctx = context.RequestContext(user,
         ctx = context.RequestContext(user,
                                      tenant,
                                      tenant,
                                      project_name=project_name,
                                      project_name=project_name,
+                                     project_domain=project_domain_name,
+                                     user_domain=user_domain_name,
                                      roles=roles,
                                      roles=roles,
                                      auth_token=auth_token,
                                      auth_token=auth_token,
                                      remote_address=remote_address,
                                      remote_address=remote_address,

+ 2 - 2
coriolis/api/v1/migrations.py

@@ -40,14 +40,14 @@ class MigrationController(api_wsgi.Controller):
         export_provider = factory.get_provider(
         export_provider = factory.get_provider(
             origin["type"], constants.PROVIDER_TYPE_EXPORT)
             origin["type"], constants.PROVIDER_TYPE_EXPORT)
         if not export_provider.validate_connection_info(
         if not export_provider.validate_connection_info(
-                origin["connection_info"]):
+                origin.get("connection_info", {})):
             # TODO: use a decent exception
             # TODO: use a decent exception
             raise exception.CoriolisException("Invalid connection info")
             raise exception.CoriolisException("Invalid connection info")
 
 
         import_provider = factory.get_provider(
         import_provider = factory.get_provider(
             destination["type"], constants.PROVIDER_TYPE_IMPORT)
             destination["type"], constants.PROVIDER_TYPE_IMPORT)
         if not import_provider.validate_connection_info(
         if not import_provider.validate_connection_info(
-                destination["connection_info"]):
+                destination.get("connection_info", {})):
             # TODO: use a decent exception
             # TODO: use a decent exception
             raise exception.CoriolisException("Invalid connection info")
             raise exception.CoriolisException("Invalid connection info")
 
 

+ 13 - 4
coriolis/db/api.py

@@ -87,21 +87,30 @@ def delete_migration(context, migration_id):
 def set_migration_status(context, migration_id, status):
 def set_migration_status(context, migration_id, status):
     migration = _soft_delete_aware_query(context, models.Migration).filter_by(
     migration = _soft_delete_aware_query(context, models.Migration).filter_by(
         project_id=context.tenant, id=migration_id).first()
         project_id=context.tenant, id=migration_id).first()
+    if not migration:
+        raise exception.NotFound("Migration not found: %s" % migration_id)
+
     migration.status = status
     migration.status = status
 
 
 
 
-@enginefacade.writer
-def set_task_status(context, task_id, status, exception_details=None):
+def _get_task(context, task_id):
     task = _soft_delete_aware_query(context, models.Task).filter_by(
     task = _soft_delete_aware_query(context, models.Task).filter_by(
         id=task_id).first()
         id=task_id).first()
+    if not task:
+        raise exception.NotFound("Task not found: %s" % task_id)
+    return task
+
+
+@enginefacade.writer
+def set_task_status(context, task_id, status, exception_details=None):
+    task = _get_task(context, task_id)
     task.status = status
     task.status = status
     task.exception_details = exception_details
     task.exception_details = exception_details
 
 
 
 
 @enginefacade.writer
 @enginefacade.writer
 def set_task_host(context, task_id, host, process_id):
 def set_task_host(context, task_id, host, process_id):
-    task = _soft_delete_aware_query(context, models.Task).filter_by(
-        id=task_id).first()
+    task = _get_task(context, task_id)
     task.host = host
     task.host = host
     task.process_id = process_id
     task.process_id = process_id
 
 

+ 3 - 2
coriolis/providers/base.py

@@ -30,7 +30,7 @@ class BaseImportProvider(Baseprovider):
     __metaclass__ = abc.ABCMeta
     __metaclass__ = abc.ABCMeta
 
 
     @abc.abstractmethod
     @abc.abstractmethod
-    def import_instance(self, connection_info, target_environment,
+    def import_instance(self, ctxt, connection_info, target_environment,
                         instance_name, export_info):
                         instance_name, export_info):
         pass
         pass
 
 
@@ -39,7 +39,8 @@ class BaseExportProvider(Baseprovider):
     __metaclass__ = abc.ABCMeta
     __metaclass__ = abc.ABCMeta
 
 
     @abc.abstractmethod
     @abc.abstractmethod
-    def export_instance(self, connection_info, instance_name, export_path):
+    def export_instance(self, ctxt, connection_info, instance_name,
+                        export_path):
         pass
         pass
 
 
 
 

+ 51 - 27
coriolis/providers/openstack/__init__.py

@@ -8,6 +8,7 @@ from keystoneauth1 import loading
 from keystoneauth1 import session
 from keystoneauth1 import session
 from neutronclient.neutron import client as neutron_client
 from neutronclient.neutron import client as neutron_client
 from novaclient import client as nova_client
 from novaclient import client as nova_client
+from oslo_config import cfg
 from oslo_log import log as logging
 from oslo_log import log as logging
 from oslo_utils import units
 from oslo_utils import units
 import paramiko
 import paramiko
@@ -18,6 +19,16 @@ from coriolis.osmorphing import manager as osmorphing_manager
 from coriolis.providers import base
 from coriolis.providers import base
 from coriolis import utils
 from coriolis import utils
 
 
+opts = [
+    cfg.StrOpt('default_auth_url',
+               default=None,
+               help='Default auth URL to be used when not specified in the'
+               ' migration\'s connection info.'),
+]
+
+CONF = cfg.CONF
+CONF.register_opts(opts, 'openstack_migration_provider')
+
 NOVA_API_VERSION = 2
 NOVA_API_VERSION = 2
 GLANCE_API_VERSION = 1
 GLANCE_API_VERSION = 1
 NEUTRON_API_VERSION = '2.0'
 NEUTRON_API_VERSION = '2.0'
@@ -95,39 +106,52 @@ class _MigrationResources(object):
 
 
 class ImportProvider(base.BaseExportProvider):
 class ImportProvider(base.BaseExportProvider):
     def validate_connection_info(self, connection_info):
     def validate_connection_info(self, connection_info):
-        keys = ["auth_url", "username", "password", "project_name"]
-        if connection_info.get("identity_api_version", 2) >= 3:
-            keys.append("domain_name")
-        return all(k in connection_info for k in keys)
+        return True
 
 
-    def _create_keystone_session(self, connection_info):
+    def _create_keystone_session(self, ctxt, connection_info):
         keystone_version = connection_info.get("identity_api_version", 2)
         keystone_version = connection_info.get("identity_api_version", 2)
-        auth_url = connection_info["auth_url"]
-        username = connection_info["username"]
-        password = connection_info["password"]
-        project_name = connection_info["project_name"]
-        domain_name = connection_info.get("domain_name")
+        auth_url = connection_info.get(
+            "auth_url", CONF.openstack_migration_provider.default_auth_url)
+
+        if not auth_url:
+            raise exception.CoriolisException(
+                '"auth_url" not provided in "connection_info" and option '
+                '"default_auth_url" in group "[openstack_migration_provider]" '
+                'not set')
+
+        username = connection_info.get("username")
+        password = connection_info.get("password")
+        project_name = connection_info.get("project_name", ctxt.project_name)
+        project_domain_name = connection_info.get(
+            "project_domain_name", ctxt.project_domain)
+        user_domain_name = connection_info.get(
+            "user_domain_name", ctxt.user_domain)
         allow_untrusted = connection_info.get("allow_untrusted", False)
         allow_untrusted = connection_info.get("allow_untrusted", False)
 
 
         # TODO: add "ca_cert" to connection_info
         # TODO: add "ca_cert" to connection_info
         verify = not allow_untrusted
         verify = not allow_untrusted
 
 
-        if keystone_version == 3:
-            loader = loading.get_plugin_loader('v3password')
-            auth = loader.load_from_options(
-                auth_url=auth_url,
-                username=username,
-                password=password,
-                user_domain_name=domain_name,
-                project_domain_name=domain_name,
-                project_name=project_name)
+        plugin_args = {
+            "auth_url": auth_url,
+            "project_name": project_name,
+        }
+
+        if username:
+            plugin_name = "password"
+            plugin_args["username"] = username
+            plugin_args["password"] = password
         else:
         else:
-            loader = loading.get_plugin_loader('password')
-            auth = loader.load_from_options(
-                auth_url=auth_url,
-                username=username,
-                password=password,
-                project_name=project_name)
+            plugin_name = "token"
+            plugin_args["token"] = ctxt.auth_token
+
+        if keystone_version == 3:
+            plugin_name = "v3" + plugin_name
+            plugin_args["project_domain_name"] = project_domain_name
+            if username:
+                plugin_args["user_domain_name"] = user_domain_name
+
+        loader = loading.get_plugin_loader(plugin_name)
+        auth = loader.load_from_options(**plugin_args)
 
 
         return session.Session(auth=auth, verify=verify)
         return session.Session(auth=auth, verify=verify)
 
 
@@ -280,9 +304,9 @@ class ImportProvider(base.BaseExportProvider):
             instance.id, volume.id, volume_dev)
             instance.id, volume.id, volume_dev)
         self._wait_for_volume(nova, volume, 'in-use')
         self._wait_for_volume(nova, volume, 'in-use')
 
 
-    def import_instance(self, connection_info, target_environment,
+    def import_instance(self, ctxt, connection_info, target_environment,
                         instance_name, export_info):
                         instance_name, export_info):
-        session = self._create_keystone_session(connection_info)
+        session = self._create_keystone_session(ctxt, connection_info)
 
 
         nova = nova_client.Client(NOVA_API_VERSION, session=session)
         nova = nova_client.Client(NOVA_API_VERSION, session=session)
         glance = glance_client.Client(GLANCE_API_VERSION, session=session)
         glance = glance_client.Client(GLANCE_API_VERSION, session=session)

+ 2 - 1
coriolis/providers/vmware_vsphere/__init__.py

@@ -242,7 +242,8 @@ class ExportProvider(base.BaseExportProvider):
             else:
             else:
                 time.sleep(.1)
                 time.sleep(.1)
 
 
-    def export_instance(self, connection_info, instance_name, export_path):
+    def export_instance(self, ctxt, connection_info, instance_name,
+                        export_path):
         host = connection_info["host"]
         host = connection_info["host"]
         port = connection_info.get("port", 443)
         port = connection_info.get("port", 443)
         username = connection_info["username"]
         username = connection_info["username"]

+ 9 - 8
coriolis/worker/rpc/server.py

@@ -112,7 +112,7 @@ class WorkerServerEndpoint(object):
 
 
                 new_task_info = self._exec_task_process(
                 new_task_info = self._exec_task_process(
                     ctxt, task_id, _export_instance,
                     ctxt, task_id, _export_instance,
-                    (provider, origin["connection_info"],
+                    (provider, origin.get("connection_info", {}),
                      instance, export_path))
                      instance, export_path))
 
 
                 new_task_info[TMP_DIRS_KEY] = [export_path]
                 new_task_info[TMP_DIRS_KEY] = [export_path]
@@ -123,7 +123,7 @@ class WorkerServerEndpoint(object):
 
 
                 self._exec_task_process(
                 self._exec_task_process(
                     ctxt, task_id, _import_instance,
                     ctxt, task_id, _import_instance,
-                    (provider, destination["connection_info"],
+                    (provider, destination.get("connection_info", {}),
                      destination["target_environment"],
                      destination["target_environment"],
                      instance, task_info))
                      instance, task_info))
             else:
             else:
@@ -154,9 +154,9 @@ def _export_instance(provider, connection_info, instance, export_path,
         progress_update_manager = _ConductorProgressUpdateManager(ctxt,
         progress_update_manager = _ConductorProgressUpdateManager(ctxt,
                                                                   task_id)
                                                                   task_id)
         provider.set_progress_update_manager(progress_update_manager)
         provider.set_progress_update_manager(progress_update_manager)
-        vm_info = provider.export_instance(connection_info, instance,
-                                           export_path)
-        mp_q.put(vm_info)
+        result = provider.export_instance(ctxt, connection_info, instance,
+                                          export_path)
+        mp_q.put(result)
     except Exception as ex:
     except Exception as ex:
         mp_q.put(str(ex))
         mp_q.put(str(ex))
         LOG.exception(ex)
         LOG.exception(ex)
@@ -171,9 +171,10 @@ def _import_instance(provider, connection_info, target_environment, instance,
         progress_update_manager = _ConductorProgressUpdateManager(ctxt,
         progress_update_manager = _ConductorProgressUpdateManager(ctxt,
                                                                   task_id)
                                                                   task_id)
         provider.set_progress_update_manager(progress_update_manager)
         provider.set_progress_update_manager(progress_update_manager)
-        provider.import_instance(connection_info, target_environment,
-                                 instance, export_info)
-        mp_q.put(None)
+        result = provider.import_instance(ctxt, connection_info,
+                                          target_environment, instance,
+                                          export_info)
+        mp_q.put(result)
     except Exception as ex:
     except Exception as ex:
         mp_q.put(str(ex))
         mp_q.put(str(ex))
         LOG.exception(ex)
         LOG.exception(ex)

+ 3 - 0
etc/coriolis/coriolis.conf

@@ -11,3 +11,6 @@ password = mysecret
 user_domain_id = default
 user_domain_id = default
 project_name = service
 project_name = service
 project_domain_id = default
 project_domain_id = default
+
+[openstack_migration_provider]
+default_auth_url = http://127.0.0.1:5000/v2.0