Procházet zdrojové kódy

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

Nashwan Azhari před 7 roky
rodič
revize
057737328b

+ 45 - 0
coriolis/api/v1/endpoint_source_options.py

@@ -0,0 +1,45 @@
+# Copyright 2019 Cloudbase Solutions Srl
+# All Rights Reserved.
+
+from oslo_log import log as logging
+
+from coriolis import utils
+from coriolis.api.v1.views import endpoint_source_options_view
+from coriolis.api import wsgi as api_wsgi
+from coriolis.endpoint_source_options import api
+from coriolis.policies import endpoints as endpoint_policies
+
+
+LOG = logging.getLogger(__name__)
+
+
+class EndpointSourceOptionsController(api_wsgi.Controller):
+    def __init__(self):
+        self._source_options_api = api.API()
+        super(EndpointSourceOptionsController, self).__init__()
+
+    def index(self, req, endpoint_id):
+        context = req.environ['coriolis.context']
+        context.can("%s:list_source_options" % (
+            endpoint_policies.ENDPOINTS_POLICY_PREFIX))
+
+        env = req.GET.get("env")
+        if env is not None:
+            env = utils.decode_base64_param(env, is_json=True)
+        else:
+            env = {}
+
+        options = req.GET.get("options")
+        if options is not None:
+            options = utils.decode_base64_param(options, is_json=True)
+        else:
+            options = {}
+
+        return endpoint_source_options_view.collection(
+            req,
+            self._source_options_api.get_endpoint_source_options(
+                context, endpoint_id, env=env, option_names=options))
+
+
+def create_resource():
+    return api_wsgi.Resource(EndpointSourceOptionsController())

+ 8 - 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_source_options
 from coriolis.api.v1 import endpoint_storage
 from coriolis.api.v1 import endpoints
 from coriolis.api.v1 import migration_actions
@@ -78,6 +79,13 @@ class APIRouter(api.APIRouter):
                         controller=(
                             self.resources['endpoint_destination_options']))
 
+        self.resources['endpoint_source_options'] = \
+            endpoint_source_options.create_resource()
+        mapper.resource('source_options',
+                        'endpoints/{endpoint_id}/source-options',
+                        controller=(
+                            self.resources['endpoint_source_options']))
+
         self.resources['provider_schemas'] = \
             provider_schemas.create_resource()
         mapper.resource('provider_schemas',

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

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

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

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

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

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

+ 0 - 0
coriolis/endpoint_source_options/__init__.py


+ 14 - 0
coriolis/endpoint_source_options/api.py

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

+ 11 - 1
coriolis/policies/endpoints.py

@@ -128,7 +128,6 @@ ENDPOINTS_POLICY_DEFAULT_RULES = [
             }
         ]
     ),
-
     policy.DocumentedRuleDefault(
         get_endpoints_policy_label('list_destination_options'),
         ENDPOINTS_POLICY_DEFAULT_RULE,
@@ -139,6 +138,17 @@ ENDPOINTS_POLICY_DEFAULT_RULES = [
                 "method": "GET"
             }
         ]
+    ),
+    policy.DocumentedRuleDefault(
+        get_endpoints_policy_label('list_source_options'),
+        ENDPOINTS_POLICY_DEFAULT_RULE,
+        "List available source options for endpoint",
+        [
+            {
+                "path": "/endpoint/{endpoint_id}/source-options",
+                "method": "GET"
+            }
+        ]
     )
 ]
 

+ 4 - 0
coriolis/schemas.py

@@ -27,6 +27,7 @@ _CORIOLIS_OS_MORPHING_RES_SCHEMA_NAME = "os_morphing_resources_schema.json"
 _CORIOLIS_VM_NETWORK_SCHEMA_NAME = "vm_network_schema.json"
 _SCHEDULE_API_BODY_SCHEMA_NAME = "replica_schedule_schema.json"
 _CORIOLIS_DESTINATION_OPTIONS_SCHEMA_NAME = "destination_options_schema.json"
+_CORIOLIS_SOURCE_OPTIONS_SCHEMA_NAME = "source_options_schema.json"
 _CORIOLIS_NETWORK_MAP_SCHEMA_NAME = "network_map_schema.json"
 _CORIOLIS_STORAGE_MAPPINGS_SCHEMA_NAME = "storage_mappings_schema.json"
 _CORIOLIS_VM_STORAGE_SCHEMA_NAME = "vm_storage_schema.json"
@@ -90,6 +91,9 @@ SCHEDULE_API_BODY_SCHEMA = get_schema(
 CORIOLIS_DESTINATION_ENVIRONMENT_OPTIONS_SCHEMA = get_schema(
     __name__, _CORIOLIS_DESTINATION_OPTIONS_SCHEMA_NAME)
 
+CORIOLIS_SOURCE_ENVIRONMENT_OPTIONS_SCHEMA = get_schema(
+    __name__, _CORIOLIS_SOURCE_OPTIONS_SCHEMA_NAME)
+
 CORIOLIS_NETWORK_MAP_SCHEMA = get_schema(
     __name__, _CORIOLIS_NETWORK_MAP_SCHEMA_NAME)
 

+ 22 - 0
coriolis/schemas/source_options_schema.json

@@ -0,0 +1,22 @@
+{
+  "$schema": "http://cloudbase.it/coriolis/schemas/endpoint_source_options#",
+  "type": "array",
+  "items": {
+    "type:": "object",
+    "properties": {
+      "name": {
+        "type": "string",
+        "help": "The name of the source environment parameter."
+      },
+      "values": {
+        "type": "array",
+        "help": "A list of possible values of the given source environment parameter."
+      },
+      "config_default": {
+        "help": "The default value set in the Coriolis service's configuration file."
+      }
+    },
+    "required": ["name", "values"],
+    "additionalProperties": false
+  }
+}

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

@@ -70,6 +70,15 @@ class WorkerClient(object):
             env=env,
             option_names=option_names)
 
+    def get_endpoint_source_options(
+            self, ctxt, platform_name, connection_info, env, option_names):
+        return self._client.call(
+            ctxt, 'get_endpoint_source_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):
         return self._client.call(
             ctxt, 'get_endpoint_networks',

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

@@ -286,6 +286,23 @@ class WorkerServerEndpoint(object):
 
         return options
 
+    def get_endpoint_source_options(
+            self, ctxt, platform_name, connection_info, env, option_names):
+        provider = providers_factory.get_provider(
+            platform_name,
+            constants.PROVIDER_TYPE_SOURCE_ENDPOINT_OPTIONS, None)
+
+        secret_connection_info = utils.get_secret_connection_info(
+            ctxt, connection_info)
+
+        options = provider.get_source_environment_options(
+            ctxt, secret_connection_info, env=env, option_names=option_names)
+
+        schemas.validate_value(
+            options, schemas.CORIOLIS_SOURCE_ENVIRONMENT_OPTIONS_SCHEMA)
+
+        return options
+
     def get_endpoint_networks(self, ctxt, platform_name, connection_info, env):
         env = env or {}
         provider = providers_factory.get_provider(