1.What is the problem? In nova-apigw, there are some servers with volume attachments are not implemented: * List volume attachments for an instance; * Attach a volume to an instance; * Show a detail of a volume attachment; * Update a volume attachment; * Detach a volume from an instance. 2.What is the solution to the problem? This patch add three feature: * We can get the volume identified by the attachment ID. * We can get a list of all the attacned volumes. * Detach a volume identified by the attachment ID from the given server ID. 3.What the features need to be implemented to the Tricircle to realize the solution? The above three features. Change-Id: I76ad72ca5d54fca7c2c463b5c41f2a4151f11eb8changes/92/326192/21
parent
1ba62882f8
commit
450f4e6531
@ -0,0 +1,72 @@
|
||||
# Copyright (c) 2015 Huawei Tech. Co., Ltd.
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
import pecan
|
||||
from pecan import expose
|
||||
from pecan import rest
|
||||
|
||||
from oslo_log import log as logging
|
||||
|
||||
import tricircle.common.client as t_client
|
||||
from tricircle.common import constants
|
||||
import tricircle.common.context as t_context
|
||||
from tricircle.common.i18n import _
|
||||
from tricircle.common import utils
|
||||
import tricircle.db.api as db_api
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class ServerIpsController(rest.RestController):
|
||||
|
||||
def __init__(self, project_id, server_id):
|
||||
self.project_id = project_id
|
||||
self.server_id = server_id
|
||||
self.clients = {constants.TOP: t_client.Client()}
|
||||
|
||||
def _get_client(self, pod_name=constants.TOP):
|
||||
if pod_name not in self.clients:
|
||||
self.clients[pod_name] = t_client.Client(pod_name)
|
||||
return self.clients[pod_name]
|
||||
|
||||
@expose(generic=True, template='json')
|
||||
def get_all(self, **kwargs):
|
||||
context = t_context.extract_context_from_environ()
|
||||
|
||||
server_mappings = db_api.get_server_mappings_by_top_id(
|
||||
context, self.server_id)
|
||||
if not server_mappings:
|
||||
return utils.format_nova_error(
|
||||
404, _('Server %s could not be found') % self.server_id)
|
||||
try:
|
||||
server_pod_name = server_mappings[0][0]['pod_name']
|
||||
api = self._get_client(server_pod_name).get_native_client(
|
||||
constants.RT_SERVER, context)
|
||||
resp, body = api.client.get('/servers/%s/ips' % self.server_id)
|
||||
pecan.response.status = resp.status_code
|
||||
if not body:
|
||||
return pecan.response
|
||||
else:
|
||||
return body
|
||||
except Exception as e:
|
||||
code = 500
|
||||
message = _('Fail to lists assigned IP addresses'
|
||||
'%(server_id)s: %(exception)s') % {
|
||||
'server_id': self.server_id,
|
||||
'exception': e}
|
||||
if hasattr(e, 'code'):
|
||||
code = e.code
|
||||
LOG.error(message)
|
||||
return utils.format_nova_error(code, message)
|
@ -0,0 +1,103 @@
|
||||
# Copyright (c) 2015 Huawei Tech. Co., Ltd.
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
from mock import patch
|
||||
import pecan
|
||||
import unittest
|
||||
|
||||
from oslo_utils import uuidutils
|
||||
|
||||
from novaclient import client
|
||||
|
||||
from tricircle.common import constants
|
||||
from tricircle.common import context
|
||||
from tricircle.db import api
|
||||
from tricircle.db import core
|
||||
from tricircle.db import models
|
||||
from tricircle.nova_apigw.controllers import server_ips
|
||||
|
||||
|
||||
class FakeResponse(object):
|
||||
def __new__(cls, code=500):
|
||||
cls.status = code
|
||||
cls.status_code = code
|
||||
return super(FakeResponse, cls).__new__(cls)
|
||||
|
||||
|
||||
class ServerIpsTest(unittest.TestCase):
|
||||
def setUp(self):
|
||||
core.initialize()
|
||||
core.ModelBase.metadata.create_all(core.get_engine())
|
||||
self.context = context.Context()
|
||||
self.project_id = 'test_project'
|
||||
self.context.tenant = self.project_id
|
||||
self.context.user = 'test_user'
|
||||
self.controller = server_ips.ServerIpsController(self.project_id, '')
|
||||
|
||||
def _prepare_pod(self, bottom_pod_num=1):
|
||||
t_pod = {'pod_id': 't_pod_uuid', 'pod_name': 't_region',
|
||||
'az_name': ''}
|
||||
api.create_pod(self.context, t_pod)
|
||||
b_pods = []
|
||||
if bottom_pod_num == 1:
|
||||
b_pod = {'pod_id': 'b_pod_uuid', 'pod_name': 'b_region',
|
||||
'az_name': 'b_az'}
|
||||
api.create_pod(self.context, b_pod)
|
||||
b_pods.append(b_pod)
|
||||
else:
|
||||
for i in xrange(1, bottom_pod_num + 1):
|
||||
b_pod = {'pod_id': 'b_pod_%d_uuid' % i,
|
||||
'pod_name': 'b_region_%d' % i,
|
||||
'az_name': 'b_az_%d' % i}
|
||||
api.create_pod(self.context, b_pod)
|
||||
b_pods.append(b_pod)
|
||||
return t_pod, b_pods
|
||||
|
||||
def _prepare_server(self, pod):
|
||||
t_server_id = uuidutils.generate_uuid()
|
||||
b_server_id = t_server_id
|
||||
with self.context.session.begin():
|
||||
core.create_resource(
|
||||
self.context, models.ResourceRouting,
|
||||
{'top_id': t_server_id, 'bottom_id': b_server_id,
|
||||
'pod_id': pod['pod_id'], 'project_id': self.project_id,
|
||||
'resource_type': constants.RT_SERVER})
|
||||
return t_server_id
|
||||
|
||||
def _prepare_pod_service(self, pod_id, service):
|
||||
config_dict = {'service_id': uuidutils.generate_uuid(),
|
||||
'pod_id': pod_id,
|
||||
'service_type': service,
|
||||
'service_url': 'fake_pod_service'}
|
||||
api.create_pod_service_configuration(self.context, config_dict)
|
||||
|
||||
@patch.object(pecan, 'response', new=FakeResponse)
|
||||
@patch.object(client.HTTPClient, 'get')
|
||||
@patch.object(context, 'extract_context_from_environ')
|
||||
def test_list_ips(self, mock_context, mock_get):
|
||||
mock_context.return_value = self.context
|
||||
mock_get.return_value = (FakeResponse(202), None)
|
||||
|
||||
t_pod, b_pods = self._prepare_pod()
|
||||
self._prepare_pod_service(b_pods[0]['pod_id'], constants.ST_NOVA)
|
||||
t_server_id = self._prepare_server(b_pods[0])
|
||||
self.controller.server_id = t_server_id
|
||||
res = self.controller.get_all()
|
||||
url = '/servers/%s/ips' % t_server_id
|
||||
mock_get.assert_called_once_with(url)
|
||||
self.assertEqual(202, res.status)
|
||||
|
||||
def tearDown(self):
|
||||
core.ModelBase.metadata.drop_all(core.get_engine())
|
Loading…
Reference in new issue