Просмотр исходного кода

Merge branch 'master' of bitbucket.org:cloudbase/coriolis-core

Alessandro Pilotti 8 лет назад
Родитель
Сommit
5aa13d5911

+ 10 - 0
coriolis/api/v1/endpoint_instances.py

@@ -7,6 +7,7 @@ from coriolis.api import common
 from coriolis.api import wsgi as api_wsgi
 from coriolis.api.v1.views import endpoint_instance_view
 from coriolis.endpoint_instances import api
+from coriolis import utils
 
 LOG = logging.getLogger(__name__)
 
@@ -25,6 +26,15 @@ class EndpointInstanceController(api_wsgi.Controller):
                 req.environ['coriolis.context'], endpoint_id, marker, limit,
                 instance_name_pattern))
 
+    def show(self, req, endpoint_id, id):
+        # WSGI does not allow encoded / chars (%2F) in the url
+        # See e.g.: https://github.com/pallets/flask/issues/900
+        id = utils.decode_base64_param(id)
+
+        return endpoint_instance_view.single(
+            req, self._instance_api.get_endpoint_instance(
+                req.environ['coriolis.context'], endpoint_id, id))
+
 
 def create_resource():
     return api_wsgi.Resource(EndpointInstanceController())

+ 30 - 0
coriolis/api/v1/endpoint_networks.py

@@ -0,0 +1,30 @@
+# Copyright 2017 Cloudbase Solutions Srl
+# All Rights Reserved.
+
+from oslo_log import log as logging
+
+from coriolis.api import wsgi as api_wsgi
+from coriolis.api.v1.views import endpoint_network_view
+from coriolis.endpoint_networks import api
+from coriolis import utils
+
+LOG = logging.getLogger(__name__)
+
+
+class EndpointNetworkController(api_wsgi.Controller):
+    def __init__(self):
+        self._network_api = api.API()
+        super(EndpointNetworkController, self).__init__()
+
+    def index(self, req, endpoint_id):
+        env = req.GET.get("env")
+        if env is not None:
+            env = utils.decode_base64_param(env)
+
+        return endpoint_network_view.collection(
+            req, self._network_api.get_endpoint_networks(
+                req.environ['coriolis.context'], endpoint_id, env))
+
+
+def create_resource():
+    return api_wsgi.Resource(EndpointNetworkController())

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

@@ -7,6 +7,7 @@ from coriolis import api
 from coriolis.api.v1 import endpoint_actions
 from coriolis.api.v1 import endpoints
 from coriolis.api.v1 import endpoint_instances
+from coriolis.api.v1 import endpoint_networks
 from coriolis.api.v1 import migrations
 from coriolis.api.v1 import migration_actions
 from coriolis.api.v1 import provider_schemas
@@ -57,6 +58,11 @@ class APIRouter(api.APIRouter):
         mapper.resource('instance', 'endpoints/{endpoint_id}/instances',
                         controller=self.resources['endpoint_instances'])
 
+        self.resources['endpoint_networks'] = \
+            endpoint_networks.create_resource()
+        mapper.resource('network', 'endpoints/{endpoint_id}/networks',
+                        controller=self.resources['endpoint_networks'])
+
         self.resources['provider_schemas'] = \
             provider_schemas.create_resource()
         mapper.resource('provider_schemas',

+ 23 - 0
coriolis/api/v1/views/endpoint_network_view.py

@@ -0,0 +1,23 @@
+# Copyright 2017 Cloudbase Solutions Srl
+# All Rights Reserved.
+
+import itertools
+
+
+def _format_network(req, network, 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 network.items()))
+
+
+def single(req, network):
+    return {"network": _format_network(req, network)}
+
+
+def collection(req, networks):
+    formatted_networks = [_format_network(req, m) for m in networks]
+    return {'networks': formatted_networks}

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

@@ -46,6 +46,18 @@ class ConductorClient(object):
             limit=limit,
             instance_name_pattern=instance_name_pattern)
 
+    def get_endpoint_instance(self, ctxt, endpoint_id, instance_name):
+        return self._client.call(
+            ctxt, 'get_endpoint_instance',
+            endpoint_id=endpoint_id,
+            instance_name=instance_name)
+
+    def get_endpoint_networks(self, ctxt, endpoint_id, env):
+        return self._client.call(
+            ctxt, 'get_endpoint_networks',
+            endpoint_id=endpoint_id,
+            env=env)
+
     def validate_endpoint_connection(self, ctxt, endpoint_id):
         return self._client.call(
             ctxt, 'validate_endpoint_connection',

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

@@ -113,6 +113,18 @@ class ConductorServerEndpoint(object):
             ctxt, endpoint.type, endpoint.connection_info, marker, limit,
             instance_name_pattern)
 
+    def get_endpoint_instance(self, ctxt, endpoint_id, instance_name):
+        endpoint = self.get_endpoint(ctxt, endpoint_id)
+
+        return self._rpc_worker_client.get_endpoint_instance(
+            ctxt, endpoint.type, endpoint.connection_info, instance_name)
+
+    def get_endpoint_networks(self, ctxt, endpoint_id, env):
+        endpoint = self.get_endpoint(ctxt, endpoint_id)
+
+        return self._rpc_worker_client.get_endpoint_networks(
+            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

@@ -47,6 +47,7 @@ PROVIDER_TYPE_REPLICA_EXPORT = 8
 PROVIDER_TYPE_ENDPOINT = 16
 PROVIDER_TYPE_RESOURCES_ENDPOINT = 32
 PROVIDER_TYPE_OS_MORPHING = 64
+PROVIDER_TYPE_ENDPOINT_NETWORKS = 128
 
 DISK_FORMAT_VMDK = 'vmdk'
 DISK_FORMAT_RAW = 'raw'

+ 4 - 0
coriolis/endpoint_instances/api.py

@@ -12,3 +12,7 @@ class API(object):
                                limit=None, instance_name_pattern=None):
         return self._rpc_client.get_endpoint_instances(
             ctxt, endpoint_id, marker, limit, instance_name_pattern)
+
+    def get_endpoint_instance(self, ctxt, endpoint_id, instance_name):
+        return self._rpc_client.get_endpoint_instance(
+            ctxt, endpoint_id, instance_name)

+ 0 - 0
coriolis/endpoint_networks/__init__.py


+ 13 - 0
coriolis/endpoint_networks/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_networks(self, ctxt, endpoint_id, env):
+        return self._rpc_client.get_endpoint_networks(
+            ctxt, endpoint_id, env)

+ 15 - 0
coriolis/providers/base.py

@@ -42,6 +42,21 @@ class BaseResourcesEndpointProvider(BaseEndpointProvider):
         raise NotImplementedError(
             "The provider does not define an instance list method.")
 
+    @abc.abstractmethod
+    def get_instance(self, ctxt, connection_info, instance_name):
+        """ Returns detailed info for a given instance """
+        raise NotImplementedError()
+
+
+class BaseEndpointNetworksProvider(object):
+    """ Defines operations for endpoints networks """
+    __metaclass__ = abc.ABCMeta
+
+    @abc.abstractmethod
+    def get_networks(self, ctxt, connection_info, env):
+        """ Returns a list of networks """
+        raise NotImplementedError()
+
 
 class BaseInstanceProvider(BaseProvider):
     __metaclass__ = abc.ABCMeta

+ 2 - 0
coriolis/providers/factory.py

@@ -25,6 +25,8 @@ PROVIDER_TYPE_MAP = {
     constants.PROVIDER_TYPE_ENDPOINT: base.BaseEndpointProvider,
     constants.PROVIDER_TYPE_RESOURCES_ENDPOINT:
         base.BaseResourcesEndpointProvider,
+    constants.PROVIDER_TYPE_ENDPOINT_NETWORKS:
+        base.BaseEndpointNetworksProvider,
     constants.PROVIDER_TYPE_OS_MORPHING: base.BaseImportInstanceProvider
 }
 

+ 4 - 0
coriolis/schemas.py

@@ -24,6 +24,7 @@ PROVIDER_TARGET_ENVIRONMENT_SCHEMA_NAME = "target_environment_schema.json"
 _CORIOLIS_VM_EXPORT_INFO_SCHEMA_NAME = "vm_export_info_schema.json"
 _CORIOLIS_VM_INSTANCE_INFO_SCHEMA_NAME = "vm_instance_info_schema.json"
 _CORIOLIS_OS_MORPHING_RES_SCHEMA_NAME = "os_morphing_resources_schema.json"
+_CORIOLIS_VM_NETWORK_SCHEMA_NAME = "vm_network_schema.json"
 
 
 def get_schema(package_name, schema_name,
@@ -76,3 +77,6 @@ CORIOLIS_OS_MORPHING_RESOURCES_SCHEMA = get_schema(
 
 CORIOLIS_VM_INSTANCE_INFO_SCHEMA = get_schema(
     __name__, _CORIOLIS_VM_INSTANCE_INFO_SCHEMA_NAME)
+
+CORIOLIS_VM_NETWORK_SCHEMA = get_schema(
+    __name__, _CORIOLIS_VM_NETWORK_SCHEMA_NAME)

+ 18 - 0
coriolis/schemas/vm_network_schema.json

@@ -0,0 +1,18 @@
+{
+  "$schema": "http://cloudbase.it/coriolis/schemas/vm_network_info#",
+  "type": "object",
+  "properties": {
+    "name": {
+      "type": "string",
+      "description": "Human-readable name of th network."
+    },
+    "id": {
+      "type": "string",
+      "description": "Unique identifier of the network."
+    }
+  },
+  "required": [
+    "id",
+    "name"
+  ]
+}

+ 9 - 0
coriolis/utils.py

@@ -1,6 +1,8 @@
 # Copyright 2016 Cloudbase Solutions Srl
 # All Rights Reserved.
 
+import base64
+import binascii
 import functools
 import hashlib
 import io
@@ -381,3 +383,10 @@ def parse_int_value(value):
         return int(str(value))
     except ValueError:
         raise exception.InvalidInput("Invalid integer: %s" % value)
+
+
+def decode_base64_param(value):
+    try:
+        return base64.b64decode(value).decode()
+    except (binascii.Error, TypeError) as ex:
+        raise exception.InvalidInput(reason=str(ex))

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

@@ -41,6 +41,21 @@ class WorkerClient(object):
             limit=limit,
             instance_name_pattern=instance_name_pattern)
 
+    def get_endpoint_instance(self, ctxt, platform_name, connection_info,
+                              instance_name):
+        return self._client.call(
+            ctxt, 'get_endpoint_instance',
+            platform_name=platform_name,
+            connection_info=connection_info,
+            instance_name=instance_name)
+
+    def get_endpoint_networks(self, ctxt, platform_name, connection_info, env):
+        return self._client.call(
+            ctxt, 'get_endpoint_networks',
+            platform_name=platform_name,
+            connection_info=connection_info,
+            env=env)
+
     def validate_endpoint_connection(self, ctxt, platform_name,
                                      connection_info):
         return self._client.call(

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

@@ -184,6 +184,37 @@ class WorkerServerEndpoint(object):
 
         return instances_info
 
+    def get_endpoint_instance(self, ctxt, platform_name, connection_info,
+                              instance_name):
+        provider = providers_factory.get_provider(
+            platform_name, constants.PROVIDER_TYPE_RESOURCES_ENDPOINT, None)
+
+        secret_connection_info = utils.get_secret_connection_info(
+            ctxt, connection_info)
+
+        instance_info = provider.get_instance(
+            ctxt, secret_connection_info, instance_name)
+
+        schemas.validate_value(
+            instance_info, schemas.CORIOLIS_VM_INSTANCE_INFO_SCHEMA)
+
+        return instance_info
+
+    def get_endpoint_networks(self, ctxt, platform_name, connection_info, env):
+        provider = providers_factory.get_provider(
+            platform_name, constants.PROVIDER_TYPE_ENDPOINT_NETWORKS, None)
+
+        secret_connection_info = utils.get_secret_connection_info(
+            ctxt, connection_info)
+
+        networks_info = provider.get_networks(
+            ctxt, secret_connection_info, env)
+        for network_info in networks_info:
+            schemas.validate_value(
+                network_info, schemas.CORIOLIS_VM_NETWORK_SCHEMA)
+
+        return networks_info
+
     def get_available_providers(self, ctxt):
         return providers_factory.get_available_providers()