Procházet zdrojové kódy

Add 'GET /endpoint/{id}/destination-options' call to API.

Nashwan Azhari před 8 roky
rodič
revize
cab0f2c18a

+ 36 - 0
coriolis/api/v1/endpoint_destination_options.py

@@ -0,0 +1,36 @@
+# Copyright 2018 Cloudbase Solutions Srl
+# All Rights Reserved.
+
+from oslo_log import log as logging
+
+from coriolis.api.v1.views import endpoint_destination_options_view
+from coriolis.api import wsgi as api_wsgi
+from coriolis.endpoint_destination_options import api
+from coriolis import utils
+
+LOG = logging.getLogger(__name__)
+
+
+class EndpointDestinationOptionsController(api_wsgi.Controller):
+    def __init__(self):
+        self._destination_options_api = api.API()
+        super(EndpointDestinationOptionsController, self).__init__()
+
+    def index(self, req, endpoint_id):
+        env = req.GET.get("env")
+        if env is not None:
+            env = utils.decode_base64_param(env, is_json=True)
+
+        options = req.GET.get("options")
+        if options is not None:
+            options = utils.decode_base64_param(env, is_json=True)
+
+        return endpoint_destination_options_view.collection(
+            req,
+            self._destination_options_api.get_endpoint_destination_options(
+                req.environ['coriolis.context'], endpoint_id,
+                env=env, option_names=options))
+
+
+def create_resource():
+    return api_wsgi.Resource(EndpointDestinationOptionsController())

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

@@ -5,6 +5,7 @@ from oslo_log import log as logging
 
 
 from coriolis import api
 from coriolis import api
 from coriolis.api.v1 import endpoint_actions
 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_instances
 from coriolis.api.v1 import endpoint_networks
 from coriolis.api.v1 import endpoint_networks
 from coriolis.api.v1 import endpoints
 from coriolis.api.v1 import endpoints
@@ -64,6 +65,13 @@ class APIRouter(api.APIRouter):
         mapper.resource('network', 'endpoints/{endpoint_id}/networks',
         mapper.resource('network', 'endpoints/{endpoint_id}/networks',
                         controller=self.resources['endpoint_networks'])
                         controller=self.resources['endpoint_networks'])
 
 
+        self.resources['endpoint_destination_options'] = \
+            endpoint_destination_options.create_resource()
+        mapper.resource('destination_options',
+                        'endpoints/{endpoint_id}/destination-options',
+                        controller=(
+                            self.resources['endpoint_destination_options']))
+
         self.resources['provider_schemas'] = \
         self.resources['provider_schemas'] = \
             provider_schemas.create_resource()
             provider_schemas.create_resource()
         mapper.resource('provider_schemas',
         mapper.resource('provider_schemas',

+ 20 - 0
coriolis/api/v1/views/endpoint_destination_options_view.py

@@ -0,0 +1,20 @@
+# Copyright 2018 Cloudbase Solutions Srl
+# All Rights Reserved.
+
+import itertools
+
+
+def _format_dest_opt(req, destination_option, 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 destination_option.items()))
+
+
+def collection(req, destination_options):
+    formatted_opts = [
+        _format_dest_opt(req, opt) for opt in destination_options]
+    return {'destination_options': formatted_opts}

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

@@ -52,6 +52,13 @@ class ConductorClient(object):
             endpoint_id=endpoint_id,
             endpoint_id=endpoint_id,
             instance_name=instance_name)
             instance_name=instance_name)
 
 
+    def get_endpoint_destination_options(
+            self, ctxt, endpoint_id, env, option_names):
+        return self._client.call(
+            ctxt, 'get_endpoint_destination_options',
+            endpoint_id=endpoint_id,
+            env=env, option_names=option_names)
+
     def get_endpoint_networks(self, ctxt, endpoint_id, env):
     def get_endpoint_networks(self, ctxt, endpoint_id, env):
         return self._client.call(
         return self._client.call(
             ctxt, 'get_endpoint_networks',
             ctxt, 'get_endpoint_networks',

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

@@ -132,6 +132,13 @@ class ConductorServerEndpoint(object):
         return self._rpc_worker_client.get_endpoint_instance(
         return self._rpc_worker_client.get_endpoint_instance(
             ctxt, endpoint.type, endpoint.connection_info, instance_name)
             ctxt, endpoint.type, endpoint.connection_info, instance_name)
 
 
+    def get_endpoint_destination_options(
+            self, ctxt, endpoint_id, env, option_names):
+        endpoint = self.get_endpoint(ctxt, endpoint_id)
+
+        return self._rpc_worker_client.get_endpoint_destination_options(
+            ctxt, endpoint.type, endpoint.connection_info, env, option_names)
+
     def get_endpoint_networks(self, ctxt, endpoint_id, env):
     def get_endpoint_networks(self, ctxt, endpoint_id, env):
         endpoint = self.get_endpoint(ctxt, endpoint_id)
         endpoint = self.get_endpoint(ctxt, endpoint_id)
 
 

+ 1 - 0
coriolis/constants.py

@@ -55,6 +55,7 @@ PROVIDER_TYPE_ENDPOINT_INSTANCES = 32
 PROVIDER_TYPE_OS_MORPHING = 64
 PROVIDER_TYPE_OS_MORPHING = 64
 PROVIDER_TYPE_ENDPOINT_NETWORKS = 128
 PROVIDER_TYPE_ENDPOINT_NETWORKS = 128
 PROVIDER_TYPE_INSTANCE_FLAVOR = 256
 PROVIDER_TYPE_INSTANCE_FLAVOR = 256
+PROVIDER_TYPE_ENDPOINT_OPTIONS = 512
 
 
 DISK_FORMAT_VMDK = 'vmdk'
 DISK_FORMAT_VMDK = 'vmdk'
 DISK_FORMAT_RAW = 'raw'
 DISK_FORMAT_RAW = 'raw'

+ 0 - 0
coriolis/endpoint_destination_options/__init__.py


+ 14 - 0
coriolis/endpoint_destination_options/api.py

@@ -0,0 +1,14 @@
+# Copyright 2016 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_destination_options(
+            self, ctxt, endpoint_id, env=None, option_names=None):
+        return self._rpc_client.get_endpoint_destination_options(
+            ctxt, endpoint_id, env, option_names)

+ 3 - 0
coriolis/endpoints/api.py

@@ -26,6 +26,9 @@ class API(object):
     def get_endpoint(self, ctxt, endpoint_id):
     def get_endpoint(self, ctxt, endpoint_id):
         return self._rpc_client.get_endpoint(ctxt, endpoint_id)
         return self._rpc_client.get_endpoint(ctxt, endpoint_id)
 
 
+    def get_endpoint_options(self, ctxt, endpoint_id):
+        return self._rpc_client.get_endpoint_options(ctxt, endpoint_id)
+
     def validate_connection(self, ctxt, endpoint_id):
     def validate_connection(self, ctxt, endpoint_id):
         return self._rpc_client.validate_endpoint_connection(
         return self._rpc_client.validate_endpoint_connection(
             ctxt, endpoint_id)
             ctxt, endpoint_id)

+ 56 - 0
coriolis/providers/base.py

@@ -54,6 +54,62 @@ class BaseEndpointNetworksProvider(object, with_metaclass(abc.ABCMeta)):
         raise NotImplementedError()
         raise NotImplementedError()
 
 
 
 
+class BaseEndpointDestinationOptionsProvider(
+        object, with_metaclass(abc.ABCMeta)):
+    @abc.abstractmethod
+    def get_target_environment_options(
+            self, ctxt, connection_info, env=None, option_names=None):
+        """ Returns all possible values for the target environment options, as
+        well as any settings the options might have in the configuration files.
+
+        param env: dict: optional target environment options
+        param option_names: list(str): optional list of parameter names to show
+        values for
+
+        Example returned values for the following options:
+        schema = {
+            "properties": {
+                "migr_network": {
+                    "type": "string"
+                },
+                "security_groups": {
+                    "type": "array",
+                    "items": { "type": "string" }
+                },
+                "migr_image": {
+                    "type": "object",
+                    "properties": {
+                        "id": { "type": "string" },
+                        "name": { "type": "integer" }
+                    }
+                }
+            }
+        }
+        The provider should return:
+        options = {
+            "migr_network": {
+                "values": ["net1", "net2", "net3"], "config_default": "net2"},
+            "security_groups": {
+                "values": ["secgroup1", "secgroup2", "secgroup3"],
+                "config_default": ["secgroup2", "secgroup3"]},
+            "migr_image": {
+                "values": [
+                    {"name": "testimage1", "id": 101},
+                    {"name": "testimg2", "id": 4}],
+                "config_default": {"name": "testimg2", "id": 4}}}
+        }
+        Observations:
+            - base types such as 'integer' or 'string' are preserved
+            - 'array' types will return an array with all the options which are
+              settable through that paramter (any, all or none may be set)
+            - for fields where both a name or ID may be returned, returning the
+              name will be preferred. The provider must ensure that, if there
+              are objects with the same name, the IDs of those objects are
+              offered as an option instead of two identical names.
+        """
+        pass
+
+
 class BaseInstanceProvider(BaseProvider):
 class BaseInstanceProvider(BaseProvider):
 
 
     def get_os_morphing_tools(self, conn, osmorphing_info):
     def get_os_morphing_tools(self, conn, osmorphing_info):

+ 2 - 0
coriolis/providers/factory.py

@@ -23,6 +23,8 @@ PROVIDER_TYPE_MAP = {
     constants.PROVIDER_TYPE_IMPORT: base.BaseImportProvider,
     constants.PROVIDER_TYPE_IMPORT: base.BaseImportProvider,
     constants.PROVIDER_TYPE_REPLICA_IMPORT: base.BaseReplicaImportProvider,
     constants.PROVIDER_TYPE_REPLICA_IMPORT: base.BaseReplicaImportProvider,
     constants.PROVIDER_TYPE_ENDPOINT: base.BaseEndpointProvider,
     constants.PROVIDER_TYPE_ENDPOINT: base.BaseEndpointProvider,
+    constants.PROVIDER_TYPE_ENDPOINT_OPTIONS:
+        base.BaseEndpointDestinationOptionsProvider,
     constants.PROVIDER_TYPE_ENDPOINT_INSTANCES:
     constants.PROVIDER_TYPE_ENDPOINT_INSTANCES:
         base.BaseEndpointInstancesProvider,
         base.BaseEndpointInstancesProvider,
     constants.PROVIDER_TYPE_ENDPOINT_NETWORKS:
     constants.PROVIDER_TYPE_ENDPOINT_NETWORKS:

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

@@ -49,6 +49,15 @@ class WorkerClient(object):
             connection_info=connection_info,
             connection_info=connection_info,
             instance_name=instance_name)
             instance_name=instance_name)
 
 
+    def get_endpoint_destination_options(
+            self, ctxt, platform_name, connection_info, env, option_names):
+        return self._client.call(
+            ctxt, 'get_endpoint_destination_options',
+            platform_name=platform_name,
+            connection_info=connection_info,
+            env=env,
+            option_names=option_names)
+
     def get_endpoint_networks(self, ctxt, platform_name, connection_info, env):
     def get_endpoint_networks(self, ctxt, platform_name, connection_info, env):
         return self._client.call(
         return self._client.call(
             ctxt, 'get_endpoint_networks',
             ctxt, 'get_endpoint_networks',

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

@@ -201,6 +201,19 @@ class WorkerServerEndpoint(object):
 
 
         return instance_info
         return instance_info
 
 
+    def get_endpoint_destination_options(
+            self, ctxt, platform_name, connection_info, env, option_names):
+        provider = providers_factory.get_provider(
+            platform_name, constants.PROVIDER_TYPE_ENDPOINT_OPTIONS, None)
+
+        secret_connection_info = utils.get_secret_connection_info(
+            ctxt, connection_info)
+
+        options = provider.get_target_environment_options(
+            ctxt, secret_connection_info, env=env, option_names=option_names)
+
+        return options
+
     def get_endpoint_networks(self, ctxt, platform_name, connection_info, env):
     def get_endpoint_networks(self, ctxt, platform_name, connection_info, env):
         env = env or {}
         env = env or {}
         provider = providers_factory.get_provider(
         provider = providers_factory.get_provider(