Sfoglia il codice sorgente

Add /endpoints/{endpoint_id}/storage API path.

Arin Toaca 8 anni fa
parent
commit
fe24e86f5f

+ 34 - 0
coriolis/api/v1/endpoint_storage.py

@@ -0,0 +1,34 @@
+# Copyright 2018 Cloudbase Solutions Srl
+# All Rights Reserved.
+
+from oslo_log import log as logging
+
+from coriolis import utils
+from coriolis.api.v1.views import endpoint_storage_view
+from coriolis.api import wsgi as api_wsgi
+from coriolis.endpoint_storage import api
+from coriolis.policies import endpoints as endpoint_policies
+
+LOG = logging.getLogger(__name__)
+
+
+class EndpointStorageController(api_wsgi.Controller):
+    def __init__(self):
+        self._storage_api = api.API()
+        super(EndpointStorageController, self).__init__()
+
+    def index(self, req, endpoint_id):
+        context = req.environ['coriolis.context']
+        context.can("%s:list_storage" % (
+            endpoint_policies.ENDPOINTS_POLICY_PREFIX))
+        env = req.GET.get("env")
+        if env is not None:
+            env = utils.decode_base64_param(env, is_json=True)
+
+        return endpoint_storage_view.collection(
+            req, self._storage_api.get_endpoint_storage(
+                context, endpoint_id, env))
+
+
+def create_resource():
+    return api_wsgi.Resource(EndpointStorageController())

+ 6 - 0
coriolis/api/v1/router.py

@@ -8,6 +8,7 @@ from coriolis.api.v1 import endpoint_actions
 from coriolis.api.v1 import endpoint_destination_options
 from coriolis.api.v1 import endpoint_instances
 from coriolis.api.v1 import endpoint_networks
+from coriolis.api.v1 import endpoint_storage
 from coriolis.api.v1 import endpoints
 from coriolis.api.v1 import migration_actions
 from coriolis.api.v1 import migrations
@@ -65,6 +66,11 @@ class APIRouter(api.APIRouter):
         mapper.resource('network', 'endpoints/{endpoint_id}/networks',
                         controller=self.resources['endpoint_networks'])
 
+        self.resources['endpoint_storage'] = \
+            endpoint_storage.create_resource()
+        mapper.resource('storage', 'endpoints/{endpoint_id}/storage',
+                        controller=self.resources['endpoint_storage'])
+
         self.resources['endpoint_destination_options'] = \
             endpoint_destination_options.create_resource()
         mapper.resource('destination_options',

+ 19 - 0
coriolis/api/v1/views/endpoint_storage_view.py

@@ -0,0 +1,19 @@
+# Copyright 2018 Cloudbase Solutions Srl
+# All Rights Reserved.
+
+import itertools
+
+
+def _format_storage(req, storage, keys=None):
+    def transform(key, value):
+        if keys and key not in keys:
+            return
+        yield (key, value)
+
+    return dict(itertools.chain.from_iterable(
+        transform(k, v) for k, v in storage.items()))
+
+
+def collection(req, storages):
+    formatted_storages = [_format_storage(req, m) for m in storages]
+    return {'storage': formatted_storages}

+ 6 - 0
coriolis/conductor/rpc/client.py

@@ -77,6 +77,12 @@ class ConductorClient(object):
             endpoint_id=endpoint_id,
             env=env)
 
+    def get_endpoint_storage(self, ctxt, endpoint_id, env):
+        return self._client.call(
+            ctxt, 'get_endpoint_storage',
+            endpoint_id=endpoint_id,
+            env=env)
+
     def validate_endpoint_connection(self, ctxt, endpoint_id):
         return self._client.call(
             ctxt, 'validate_endpoint_connection',

+ 6 - 0
coriolis/conductor/rpc/server.py

@@ -151,6 +151,12 @@ class ConductorServerEndpoint(object):
         return self._rpc_worker_client.get_endpoint_networks(
             ctxt, endpoint.type, endpoint.connection_info, env)
 
+    def get_endpoint_storage(self, ctxt, endpoint_id, env):
+        endpoint = self.get_endpoint(ctxt, endpoint_id)
+
+        return self._rpc_worker_client.get_endpoint_storage(
+            ctxt, endpoint.type, endpoint.connection_info, env)
+
     def validate_endpoint_connection(self, ctxt, endpoint_id):
         endpoint = self.get_endpoint(ctxt, endpoint_id)
         return self._rpc_worker_client.validate_endpoint_connection(

+ 1 - 0
coriolis/constants.py

@@ -65,6 +65,7 @@ PROVIDER_TYPE_VALIDATE_MIGRATION_EXPORT = 2048
 PROVIDER_TYPE_VALIDATE_REPLICA_EXPORT = 4096
 PROVIDER_TYPE_VALIDATE_MIGRATION_IMPORT = 8192
 PROVIDER_TYPE_VALIDATE_REPLICA_IMPORT = 16384
+PROVIDER_TYPE_ENDPOINT_STORAGE = 32768
 
 DISK_FORMAT_VMDK = 'vmdk'
 DISK_FORMAT_RAW = 'raw'

+ 0 - 0
coriolis/endpoint_storage/__init__.py


+ 13 - 0
coriolis/endpoint_storage/api.py

@@ -0,0 +1,13 @@
+# Copyright 2017 Cloudbase Solutions Srl
+# All Rights Reserved.
+
+from coriolis.conductor.rpc import client as rpc_client
+
+
+class API(object):
+    def __init__(self):
+        self._rpc_client = rpc_client.ConductorClient()
+
+    def get_endpoint_storage(self, ctxt, endpoint_id, env):
+        return self._rpc_client.get_endpoint_storage(
+            ctxt, endpoint_id, env)

+ 12 - 0
coriolis/policies/endpoints.py

@@ -117,6 +117,18 @@ ENDPOINTS_POLICY_DEFAULT_RULES = [
             }
         ]
     ),
+    policy.DocumentedRuleDefault(
+        get_endpoints_policy_label('list_storage'),
+        ENDPOINTS_POLICY_DEFAULT_RULE,
+        "List storage types available on the given endpoint",
+        [
+            {
+                "path": "/endpoint/{endpoint_id}/storage",
+                "method": "GET"
+            }
+        ]
+    ),
+
     policy.DocumentedRuleDefault(
         get_endpoints_policy_label('list_destination_options'),
         ENDPOINTS_POLICY_DEFAULT_RULE,

+ 7 - 0
coriolis/providers/base.py

@@ -369,3 +369,10 @@ def get_os_morphing_tools_helper(conn, os_morphing_tools_clss,
         if os_info:
             return (tools, os_info)
     raise exception.OSMorphingToolsNotFound()
+
+
+class BaseEndpointStorageProvider(object, with_metaclass(abc.ABCMeta)):
+    @abc.abstractmethod
+    def get_storage(self, ctxt, connection_info):
+        """ Returns all the storage options available"""
+        pass

+ 2 - 0
coriolis/providers/factory.py

@@ -29,6 +29,8 @@ PROVIDER_TYPE_MAP = {
         base.BaseEndpointInstancesProvider,
     constants.PROVIDER_TYPE_ENDPOINT_NETWORKS:
         base.BaseEndpointNetworksProvider,
+    constants.PROVIDER_TYPE_ENDPOINT_STORAGE:
+        base.BaseEndpointStorageProvider,
     constants.PROVIDER_TYPE_OS_MORPHING: base.BaseImportInstanceProvider,
     constants.PROVIDER_TYPE_INSTANCE_FLAVOR: base.BaseInstanceFlavorProvider,
     constants.PROVIDER_TYPE_SETUP_LIBS: base.BaseProviderSetupExtraLibsMixin,

+ 7 - 0
coriolis/worker/rpc/client.py

@@ -91,6 +91,13 @@ class WorkerClient(object):
             platform_name=platform_name,
             target_env=target_env)
 
+    def get_endpoint_storage(self, ctxt, platform_name, connection_info, env):
+        return self._client.call(
+            ctxt, 'get_endpoint_storage',
+            platform_name=platform_name,
+            connection_info=connection_info,
+            env=env)
+
     def get_available_providers(self, ctxt):
         return self._client.call(
             ctxt, 'get_available_providers')

+ 12 - 0
coriolis/worker/rpc/server.py

@@ -277,6 +277,18 @@ class WorkerServerEndpoint(object):
 
         return networks_info
 
+    def get_endpoint_storage(self, ctxt, platform_name, connection_info, env):
+        provider = providers_factory.get_provider(
+            platform_name, constants.PROVIDER_TYPE_ENDPOINT_STORAGE, None)
+
+        secret_connection_info = utils.get_secret_connection_info(
+            ctxt, connection_info)
+
+        storage = provider.get_storage(
+            ctxt, secret_connection_info, env)
+
+        return storage
+
     def get_available_providers(self, ctxt):
         return providers_factory.get_available_providers()
 

+ 1 - 0
etc/coriolis/policy.yaml

@@ -12,6 +12,7 @@
 "migration:endpoints:list_instances": "rule:admin_or_owner"
 "migration:endpoints:get_instance": "rule:admin_or_owner"
 "migration:endpoints:list_networks": "rule:admin_or_owner"
+"migration:endpoints:list_storage": "rule:admin_or_owner"
 "migration:endpoints:list_destination_options": "rule:admin_or_owner"
 
 "migration:migrations:create": "rule:admin_or_owner"