Add instance type CLI
Add the CLI of instance type and entry point, update openstack request id key to offical format, modify the related unit tests. Change-Id: Ie736d99e5a9756dbae372793f39ad3c5cca72fe8
This commit is contained in:
@@ -35,13 +35,13 @@ except NameError:
|
|||||||
|
|
||||||
|
|
||||||
def getid(obj):
|
def getid(obj):
|
||||||
"""Get obj's id or object itself if no id
|
"""Get obj's uuid or object itself if no uuid
|
||||||
|
|
||||||
Abstracts the common pattern of allowing both an object or
|
Abstracts the common pattern of allowing both an object or
|
||||||
an object's ID (UUID) as a parameter when dealing with relationships.
|
an object's ID (UUID) as a parameter when dealing with relationships.
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
return obj.id
|
return obj.uuid
|
||||||
except AttributeError:
|
except AttributeError:
|
||||||
return obj
|
return obj
|
||||||
|
|
||||||
@@ -168,7 +168,7 @@ class ManagerWithFind(Manager):
|
|||||||
elif num > 1:
|
elif num > 1:
|
||||||
raise exceptions.NoUniqueMatch
|
raise exceptions.NoUniqueMatch
|
||||||
else:
|
else:
|
||||||
return self.get(matches[0].id)
|
return self.get(matches[0].uuid)
|
||||||
|
|
||||||
def findall(self, **kwargs):
|
def findall(self, **kwargs):
|
||||||
"""Find all items with attributes matching ``**kwargs``.
|
"""Find all items with attributes matching ``**kwargs``.
|
||||||
@@ -214,7 +214,7 @@ class RequestIdMixin(object):
|
|||||||
|
|
||||||
def _append_request_id(self, resp):
|
def _append_request_id(self, resp):
|
||||||
if isinstance(resp, Response):
|
if isinstance(resp, Response):
|
||||||
# Extract 'x-openstack-request-id' from headers if
|
# Extract 'X-Openstack-Request-Id' from headers if
|
||||||
# response is a Response object.
|
# response is a Response object.
|
||||||
request_id = (resp.headers.get('x-openstack-request-id') or
|
request_id = (resp.headers.get('x-openstack-request-id') or
|
||||||
resp.headers.get('x-compute-request-id'))
|
resp.headers.get('x-compute-request-id'))
|
||||||
@@ -280,7 +280,7 @@ class Resource(RequestIdMixin):
|
|||||||
if not hasattr(self.manager, 'get'):
|
if not hasattr(self.manager, 'get'):
|
||||||
return
|
return
|
||||||
|
|
||||||
new = self.manager.get(self.id)
|
new = self.manager.get(self.uuid)
|
||||||
if new:
|
if new:
|
||||||
self._add_details(new._info)
|
self._add_details(new._info)
|
||||||
# The 'request_ids' attribute has been added,
|
# The 'request_ids' attribute has been added,
|
||||||
|
|||||||
@@ -299,7 +299,7 @@ class SessionClient(adapter.LegacyJsonAdapter):
|
|||||||
kwargs.setdefault('user_agent', USER_AGENT)
|
kwargs.setdefault('user_agent', USER_AGENT)
|
||||||
|
|
||||||
if 'data' in kwargs:
|
if 'data' in kwargs:
|
||||||
kwargs['data'] = jsonutils.dumps(kwargs['data'])
|
kwargs['json'] = kwargs.pop('data')
|
||||||
|
|
||||||
resp, body = super(SessionClient, self).request(
|
resp, body = super(SessionClient, self).request(
|
||||||
url, method,
|
url, method,
|
||||||
|
|||||||
182
nimbleclient/osc/v1/instance_type.py
Normal file
182
nimbleclient/osc/v1/instance_type.py
Normal file
@@ -0,0 +1,182 @@
|
|||||||
|
# Copyright 2016 Huawei, Inc. 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.
|
||||||
|
#
|
||||||
|
|
||||||
|
|
||||||
|
"""Nimble v1 Type action implementations"""
|
||||||
|
|
||||||
|
import logging
|
||||||
|
|
||||||
|
from osc_lib.command import command
|
||||||
|
from osc_lib import exceptions
|
||||||
|
from osc_lib import utils
|
||||||
|
import six
|
||||||
|
|
||||||
|
from nimbleclient.common.i18n import _
|
||||||
|
|
||||||
|
|
||||||
|
LOG = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
class CreateType(command.ShowOne):
|
||||||
|
"""Create a new instance type"""
|
||||||
|
|
||||||
|
def get_parser(self, prog_name):
|
||||||
|
parser = super(CreateType, self).get_parser(prog_name)
|
||||||
|
parser.add_argument(
|
||||||
|
"name",
|
||||||
|
metavar="<name>",
|
||||||
|
help=_("New type name")
|
||||||
|
)
|
||||||
|
public_group = parser.add_mutually_exclusive_group()
|
||||||
|
public_group.add_argument(
|
||||||
|
"--public",
|
||||||
|
action="store_true",
|
||||||
|
help=_("Type is available to other projects (default)")
|
||||||
|
)
|
||||||
|
public_group.add_argument(
|
||||||
|
"--private",
|
||||||
|
action="store_true",
|
||||||
|
help=_("Type is not available to other projects")
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
"--description",
|
||||||
|
metavar="<description>",
|
||||||
|
help=_("Type description"),
|
||||||
|
)
|
||||||
|
return parser
|
||||||
|
|
||||||
|
def take_action(self, parsed_args):
|
||||||
|
bc_client = self.app.client_manager.baremetal_compute
|
||||||
|
|
||||||
|
info = {}
|
||||||
|
is_public = True
|
||||||
|
if parsed_args.public:
|
||||||
|
is_public = True
|
||||||
|
if parsed_args.private:
|
||||||
|
is_public = False
|
||||||
|
|
||||||
|
data = bc_client.instance_type.create(
|
||||||
|
name=parsed_args.name,
|
||||||
|
is_public=is_public,
|
||||||
|
description=parsed_args.description,
|
||||||
|
)
|
||||||
|
info.update(data._info)
|
||||||
|
|
||||||
|
return zip(*sorted(six.iteritems(info)))
|
||||||
|
|
||||||
|
|
||||||
|
class DeleteType(command.Command):
|
||||||
|
"""Delete existing instance type(s)"""
|
||||||
|
|
||||||
|
def get_parser(self, prog_name):
|
||||||
|
parser = super(DeleteType, self).get_parser(prog_name)
|
||||||
|
parser.add_argument(
|
||||||
|
'type',
|
||||||
|
metavar='<type>',
|
||||||
|
nargs='+',
|
||||||
|
help=_("Type(s) to delete (name or UUID)")
|
||||||
|
)
|
||||||
|
return parser
|
||||||
|
|
||||||
|
def take_action(self, parsed_args):
|
||||||
|
bc_client = self.app.client_manager.baremetal_compute
|
||||||
|
result = 0
|
||||||
|
for one_type in parsed_args.type:
|
||||||
|
try:
|
||||||
|
data = utils.find_resource(
|
||||||
|
bc_client.instance_type, one_type)
|
||||||
|
bc_client.instance_type.delete(data.uuid)
|
||||||
|
except Exception as e:
|
||||||
|
result += 1
|
||||||
|
LOG.error(_("Failed to delete type with name or UUID "
|
||||||
|
"'%(type)s': %(e)s") % {'type': one_type, 'e': e})
|
||||||
|
|
||||||
|
if result > 0:
|
||||||
|
total = len(parsed_args.type)
|
||||||
|
msg = (_("%(result)s of %(total)s types failed "
|
||||||
|
"to delete.") % {'result': result, 'total': total})
|
||||||
|
raise exceptions.CommandError(msg)
|
||||||
|
|
||||||
|
|
||||||
|
class ListType(command.Lister):
|
||||||
|
"""List all types"""
|
||||||
|
|
||||||
|
def get_parser(self, prog_name):
|
||||||
|
parser = super(ListType, self).get_parser(prog_name)
|
||||||
|
parser.add_argument(
|
||||||
|
'--long',
|
||||||
|
action='store_true',
|
||||||
|
default=False,
|
||||||
|
help=_("List additional fields in output")
|
||||||
|
)
|
||||||
|
return parser
|
||||||
|
|
||||||
|
def take_action(self, parsed_args):
|
||||||
|
bc_client = self.app.client_manager.baremetal_compute
|
||||||
|
|
||||||
|
data = bc_client.instance_type.list()
|
||||||
|
|
||||||
|
if parsed_args.long:
|
||||||
|
# This is the easiest way to change column headers
|
||||||
|
column_headers = (
|
||||||
|
"UUID",
|
||||||
|
"Name",
|
||||||
|
"Is Public",
|
||||||
|
"Description",
|
||||||
|
"Properties",
|
||||||
|
)
|
||||||
|
columns = (
|
||||||
|
"UUID",
|
||||||
|
"Name",
|
||||||
|
"Is Public",
|
||||||
|
"Description",
|
||||||
|
"Extra Specs",
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
column_headers = columns = (
|
||||||
|
"UUID",
|
||||||
|
"Name",
|
||||||
|
"Is Public",
|
||||||
|
)
|
||||||
|
|
||||||
|
return (column_headers,
|
||||||
|
(utils.get_item_properties(
|
||||||
|
s, columns,
|
||||||
|
) for s in data))
|
||||||
|
|
||||||
|
|
||||||
|
class ShowType(command.ShowOne):
|
||||||
|
"""Display instance type details"""
|
||||||
|
|
||||||
|
def get_parser(self, prog_name):
|
||||||
|
parser = super(ShowType, self).get_parser(prog_name)
|
||||||
|
parser.add_argument(
|
||||||
|
'type',
|
||||||
|
metavar='<type>',
|
||||||
|
help=_("Type to display (name or UUID)")
|
||||||
|
)
|
||||||
|
return parser
|
||||||
|
|
||||||
|
def take_action(self, parsed_args):
|
||||||
|
|
||||||
|
bc_client = self.app.client_manager.baremetal_compute
|
||||||
|
data = utils.find_resource(
|
||||||
|
bc_client.instance_type,
|
||||||
|
parsed_args.type,
|
||||||
|
)
|
||||||
|
|
||||||
|
info = {}
|
||||||
|
info.update(data._info)
|
||||||
|
return zip(*sorted(six.iteritems(info)))
|
||||||
@@ -16,7 +16,19 @@
|
|||||||
|
|
||||||
from osc_lib.tests import utils
|
from osc_lib.tests import utils
|
||||||
|
|
||||||
|
from nimbleclient.tests.unit import fakes
|
||||||
|
|
||||||
|
|
||||||
class TestBase(utils.TestCommand):
|
class TestBase(utils.TestCommand):
|
||||||
"""Test case base class for all unit tests."""
|
"""Test case base class for all unit tests."""
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class TestBaremetalComputeV1(TestBase):
|
||||||
|
"""Test case base class for the unit tests of Baremetal Compute V1 API."""
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
super(TestBaremetalComputeV1, self).setUp()
|
||||||
|
|
||||||
|
fake_client = fakes.FakeBaremetalComputeV1Client()
|
||||||
|
self.app.client_manager.baremetal_compute = fake_client
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ class TestResource(test_base.TestBase):
|
|||||||
self.assertEqual(4, base.getid(4))
|
self.assertEqual(4, base.getid(4))
|
||||||
|
|
||||||
class TmpObject(object):
|
class TmpObject(object):
|
||||||
id = 4
|
uuid = 4
|
||||||
self.assertEqual(4, base.getid(TmpObject))
|
self.assertEqual(4, base.getid(TmpObject))
|
||||||
|
|
||||||
def test_init_with_attribute_info(self):
|
def test_init_with_attribute_info(self):
|
||||||
@@ -46,13 +46,13 @@ class TestResource(test_base.TestBase):
|
|||||||
|
|
||||||
def test_resource_lazy_getattr(self):
|
def test_resource_lazy_getattr(self):
|
||||||
fake_manager = mock.Mock()
|
fake_manager = mock.Mock()
|
||||||
return_resource = base.Resource(None, dict(id=mock.sentinel.fake_id,
|
return_resource = base.Resource(None, dict(uuid=mock.sentinel.fake_id,
|
||||||
foo='bar',
|
foo='bar',
|
||||||
name='fake_name'))
|
name='fake_name'))
|
||||||
fake_manager.get.return_value = return_resource
|
fake_manager.get.return_value = return_resource
|
||||||
|
|
||||||
r = base.Resource(fake_manager,
|
r = base.Resource(fake_manager,
|
||||||
dict(id=mock.sentinel.fake_id, foo='bar'))
|
dict(uuid=mock.sentinel.fake_id, foo='bar'))
|
||||||
self.assertTrue(hasattr(r, 'foo'))
|
self.assertTrue(hasattr(r, 'foo'))
|
||||||
self.assertEqual('bar', r.foo)
|
self.assertEqual('bar', r.foo)
|
||||||
self.assertFalse(r.is_loaded())
|
self.assertFalse(r.is_loaded())
|
||||||
@@ -73,7 +73,7 @@ class TestResource(test_base.TestBase):
|
|||||||
|
|
||||||
# Two resources of different types: never equal
|
# Two resources of different types: never equal
|
||||||
r1 = base.Resource(None, {'id': 1})
|
r1 = base.Resource(None, {'id': 1})
|
||||||
r2 = fakes.FaksResource(None, {'id': 1})
|
r2 = fakes.FakeResource(None, {'id': 1})
|
||||||
self.assertNotEqual(r1, r2)
|
self.assertNotEqual(r1, r2)
|
||||||
|
|
||||||
# Two resources with no ID: equal if their info is equal
|
# Two resources with no ID: equal if their info is equal
|
||||||
@@ -99,8 +99,8 @@ class TestManager(test_base.TestBase):
|
|||||||
def test_manager_get(self, mock_get):
|
def test_manager_get(self, mock_get):
|
||||||
mock_get.return_value = (fakes.create_response_obj_with_header(),
|
mock_get.return_value = (fakes.create_response_obj_with_header(),
|
||||||
mock.MagicMock())
|
mock.MagicMock())
|
||||||
fake_resource = fakes.FaksResource(
|
fake_resource = fakes.FakeResource(
|
||||||
None, dict(id=fakes.FAKE_RESOURCE_ID,
|
None, dict(uuid=fakes.FAKE_RESOURCE_ID,
|
||||||
name=fakes.FAKE_RESOURCE_NAME))
|
name=fakes.FAKE_RESOURCE_NAME))
|
||||||
result = self.fake_manager.get(fake_resource)
|
result = self.fake_manager.get(fake_resource)
|
||||||
self.assertIsInstance(result, base.Resource)
|
self.assertIsInstance(result, base.Resource)
|
||||||
@@ -123,8 +123,8 @@ class TestManager(test_base.TestBase):
|
|||||||
def test_manager_update(self, mock_patch):
|
def test_manager_update(self, mock_patch):
|
||||||
mock_patch.return_value = (fakes.create_response_obj_with_header(),
|
mock_patch.return_value = (fakes.create_response_obj_with_header(),
|
||||||
mock.MagicMock())
|
mock.MagicMock())
|
||||||
fake_resource = fakes.FaksResource(
|
fake_resource = fakes.FakeResource(
|
||||||
None, dict(id=fakes.FAKE_RESOURCE_ID,
|
None, dict(uuid=fakes.FAKE_RESOURCE_ID,
|
||||||
name=fakes.FAKE_RESOURCE_NAME))
|
name=fakes.FAKE_RESOURCE_NAME))
|
||||||
result = self.fake_manager.update(fake_resource)
|
result = self.fake_manager.update(fake_resource)
|
||||||
self.assertIsInstance(result, base.Resource)
|
self.assertIsInstance(result, base.Resource)
|
||||||
@@ -138,8 +138,8 @@ class TestManager(test_base.TestBase):
|
|||||||
def test_manager_delete(self, mock_delete):
|
def test_manager_delete(self, mock_delete):
|
||||||
mock_delete.return_value = (fakes.create_response_obj_with_header(),
|
mock_delete.return_value = (fakes.create_response_obj_with_header(),
|
||||||
None)
|
None)
|
||||||
fake_resource = fakes.FaksResource(
|
fake_resource = fakes.FakeResource(
|
||||||
None, dict(id=fakes.FAKE_RESOURCE_ID,
|
None, dict(uuid=fakes.FAKE_RESOURCE_ID,
|
||||||
name=fakes.FAKE_RESOURCE_NAME))
|
name=fakes.FAKE_RESOURCE_NAME))
|
||||||
result = self.fake_manager.delete(fake_resource)
|
result = self.fake_manager.delete(fake_resource)
|
||||||
self.assertIsInstance(result, base.TupleWithMeta)
|
self.assertIsInstance(result, base.TupleWithMeta)
|
||||||
@@ -151,8 +151,8 @@ class TestManager(test_base.TestBase):
|
|||||||
def test_manager_create(self, mock_post):
|
def test_manager_create(self, mock_post):
|
||||||
mock_post.return_value = (fakes.create_response_obj_with_header(),
|
mock_post.return_value = (fakes.create_response_obj_with_header(),
|
||||||
mock.MagicMock())
|
mock.MagicMock())
|
||||||
fake_resource = fakes.FaksResource(
|
fake_resource = fakes.FakeResource(
|
||||||
None, dict(id=fakes.FAKE_RESOURCE_ID,
|
None, dict(uuid=fakes.FAKE_RESOURCE_ID,
|
||||||
name=fakes.FAKE_RESOURCE_NAME))
|
name=fakes.FAKE_RESOURCE_NAME))
|
||||||
result = self.fake_manager.create(fake_resource)
|
result = self.fake_manager.create(fake_resource)
|
||||||
self.assertIsInstance(result, base.Resource)
|
self.assertIsInstance(result, base.Resource)
|
||||||
@@ -164,9 +164,9 @@ class TestManager(test_base.TestBase):
|
|||||||
|
|
||||||
@mock.patch.object(fakes.FakeHTTPClient, 'get')
|
@mock.patch.object(fakes.FakeHTTPClient, 'get')
|
||||||
def test_manager_find(self, mock_get):
|
def test_manager_find(self, mock_get):
|
||||||
fake_json_body_1 = dict(id=fakes.FAKE_RESOURCE_ID,
|
fake_json_body_1 = dict(uuid=fakes.FAKE_RESOURCE_ID,
|
||||||
name=fakes.FAKE_RESOURCE_NAME)
|
name=fakes.FAKE_RESOURCE_NAME)
|
||||||
fake_json_body_2 = dict(id='no_existed_id',
|
fake_json_body_2 = dict(uuid='no_existed_id',
|
||||||
name='no_existed_name')
|
name='no_existed_name')
|
||||||
mock_get.side_effect = [
|
mock_get.side_effect = [
|
||||||
(fakes.create_response_obj_with_header(),
|
(fakes.create_response_obj_with_header(),
|
||||||
@@ -175,10 +175,10 @@ class TestManager(test_base.TestBase):
|
|||||||
(fakes.create_response_obj_with_header(),
|
(fakes.create_response_obj_with_header(),
|
||||||
fake_json_body_1)
|
fake_json_body_1)
|
||||||
]
|
]
|
||||||
result = self.fake_manager.find(id=fakes.FAKE_RESOURCE_ID,
|
result = self.fake_manager.find(uuid=fakes.FAKE_RESOURCE_ID,
|
||||||
name=fakes.FAKE_RESOURCE_NAME)
|
name=fakes.FAKE_RESOURCE_NAME)
|
||||||
self.assertIsInstance(result, base.Resource)
|
self.assertIsInstance(result, base.Resource)
|
||||||
self.assertEqual(fakes.FAKE_RESOURCE_ID, result.id)
|
self.assertEqual(fakes.FAKE_RESOURCE_ID, result.uuid)
|
||||||
self.assertEqual(fakes.FAKE_RESOURCE_NAME, result.name)
|
self.assertEqual(fakes.FAKE_RESOURCE_NAME, result.name)
|
||||||
self.assertTrue(result.is_loaded())
|
self.assertTrue(result.is_loaded())
|
||||||
expect_collection_url = fakes.FAKE_RESOURCE_COLLECTION_URL
|
expect_collection_url = fakes.FAKE_RESOURCE_COLLECTION_URL
|
||||||
@@ -194,14 +194,14 @@ class TestManager(test_base.TestBase):
|
|||||||
{'resources': []})
|
{'resources': []})
|
||||||
self.assertRaises(exceptions.NotFound,
|
self.assertRaises(exceptions.NotFound,
|
||||||
self.fake_manager.find,
|
self.fake_manager.find,
|
||||||
id=fakes.FAKE_RESOURCE_ID,
|
uuid=fakes.FAKE_RESOURCE_ID,
|
||||||
name=fakes.FAKE_RESOURCE_NAME)
|
name=fakes.FAKE_RESOURCE_NAME)
|
||||||
expect_collection_url = fakes.FAKE_RESOURCE_COLLECTION_URL
|
expect_collection_url = fakes.FAKE_RESOURCE_COLLECTION_URL
|
||||||
mock_get.assert_called_once_with(expect_collection_url, headers={})
|
mock_get.assert_called_once_with(expect_collection_url, headers={})
|
||||||
|
|
||||||
@mock.patch.object(fakes.FakeHTTPClient, 'get')
|
@mock.patch.object(fakes.FakeHTTPClient, 'get')
|
||||||
def test_manager_find_more_than_one_result(self, mock_get):
|
def test_manager_find_more_than_one_result(self, mock_get):
|
||||||
fake_json_body_1 = dict(id=fakes.FAKE_RESOURCE_ID,
|
fake_json_body_1 = dict(uuid=fakes.FAKE_RESOURCE_ID,
|
||||||
name=fakes.FAKE_RESOURCE_NAME)
|
name=fakes.FAKE_RESOURCE_NAME)
|
||||||
fake_json_body_2 = copy.deepcopy(fake_json_body_1)
|
fake_json_body_2 = copy.deepcopy(fake_json_body_1)
|
||||||
mock_get.return_value = (fakes.create_response_obj_with_header(),
|
mock_get.return_value = (fakes.create_response_obj_with_header(),
|
||||||
@@ -209,7 +209,7 @@ class TestManager(test_base.TestBase):
|
|||||||
fake_json_body_2]})
|
fake_json_body_2]})
|
||||||
self.assertRaises(exceptions.NoUniqueMatch,
|
self.assertRaises(exceptions.NoUniqueMatch,
|
||||||
self.fake_manager.find,
|
self.fake_manager.find,
|
||||||
id=fakes.FAKE_RESOURCE_ID,
|
uuid=fakes.FAKE_RESOURCE_ID,
|
||||||
name=fakes.FAKE_RESOURCE_NAME)
|
name=fakes.FAKE_RESOURCE_NAME)
|
||||||
expect_collection_url = fakes.FAKE_RESOURCE_COLLECTION_URL
|
expect_collection_url = fakes.FAKE_RESOURCE_COLLECTION_URL
|
||||||
mock_get.assert_called_once_with(expect_collection_url, headers={})
|
mock_get.assert_called_once_with(expect_collection_url, headers={})
|
||||||
|
|||||||
@@ -633,7 +633,7 @@ class TestSessionClient(base.TestBase):
|
|||||||
resp, body = client.request('', 'GET', **kwargs)
|
resp, body = client.request('', 'GET', **kwargs)
|
||||||
|
|
||||||
self.assertEqual({'endpoint_override': 'http://no.where/',
|
self.assertEqual({'endpoint_override': 'http://no.where/',
|
||||||
'data': '"some_data"',
|
'json': 'some_data',
|
||||||
'user_agent': 'python-nimbleclient',
|
'user_agent': 'python-nimbleclient',
|
||||||
'raise_exc': False}, self.request.call_args[1])
|
'raise_exc': False}, self.request.call_args[1])
|
||||||
self.assertEqual(200, resp.status_code)
|
self.assertEqual(200, resp.status_code)
|
||||||
@@ -655,7 +655,7 @@ class TestSessionClient(base.TestBase):
|
|||||||
resp, body = client.request('', 'GET', **kwargs)
|
resp, body = client.request('', 'GET', **kwargs)
|
||||||
|
|
||||||
self.assertEqual({'endpoint_override': 'http://no.where/',
|
self.assertEqual({'endpoint_override': 'http://no.where/',
|
||||||
'data': "{'files': test}}",
|
'json': {'files': data},
|
||||||
'user_agent': 'python-nimbleclient',
|
'user_agent': 'python-nimbleclient',
|
||||||
'raise_exc': False}, self.request.call_args[1])
|
'raise_exc': False}, self.request.call_args[1])
|
||||||
self.assertEqual(200, resp.status_code)
|
self.assertEqual(200, resp.status_code)
|
||||||
|
|||||||
@@ -13,10 +13,15 @@
|
|||||||
# under the License.
|
# under the License.
|
||||||
#
|
#
|
||||||
|
|
||||||
|
import copy
|
||||||
|
import uuid
|
||||||
|
|
||||||
|
import mock
|
||||||
from oslo_serialization import jsonutils
|
from oslo_serialization import jsonutils
|
||||||
from requests import Response
|
from requests import Response
|
||||||
|
|
||||||
from nimbleclient.common import base
|
from nimbleclient.common import base
|
||||||
|
from nimbleclient.v1 import instance_type
|
||||||
|
|
||||||
# fake request id
|
# fake request id
|
||||||
FAKE_REQUEST_ID = 'req-0594c66b-6973-405c-ae2c-43fcfc00f2e3'
|
FAKE_REQUEST_ID = 'req-0594c66b-6973-405c-ae2c-43fcfc00f2e3'
|
||||||
@@ -47,6 +52,16 @@ def create_resource_manager():
|
|||||||
return FakeManager()
|
return FakeManager()
|
||||||
|
|
||||||
|
|
||||||
|
class FakeBaremetalComputeV1Client(object):
|
||||||
|
|
||||||
|
def __init__(self, **kwargs):
|
||||||
|
self.fake_http_client = mock.Mock()
|
||||||
|
|
||||||
|
self.instance_type = instance_type.InstanceTypeManager(
|
||||||
|
self.fake_http_client)
|
||||||
|
self.instance = None
|
||||||
|
|
||||||
|
|
||||||
class FakeHTTPClient(object):
|
class FakeHTTPClient(object):
|
||||||
|
|
||||||
def get(self):
|
def get(self):
|
||||||
@@ -68,12 +83,12 @@ class FakeHTTPClient(object):
|
|||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
class FaksResource(base.Resource):
|
class FakeResource(base.Resource):
|
||||||
id = 'N/A'
|
pass
|
||||||
|
|
||||||
|
|
||||||
class FakeManager(base.ManagerWithFind):
|
class FakeManager(base.ManagerWithFind):
|
||||||
resource_class = FaksResource
|
resource_class = FakeResource
|
||||||
|
|
||||||
def __init__(self, api=None):
|
def __init__(self, api=None):
|
||||||
if not api:
|
if not api:
|
||||||
@@ -129,3 +144,75 @@ class FakeHTTPResponse(object):
|
|||||||
|
|
||||||
def json(self):
|
def json(self):
|
||||||
return jsonutils.loads(self.content)
|
return jsonutils.loads(self.content)
|
||||||
|
|
||||||
|
|
||||||
|
class FakeInstanceType(object):
|
||||||
|
"""Fake one instance type."""
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def create_one_instance_type(attrs=None):
|
||||||
|
"""Create a fake instance type.
|
||||||
|
|
||||||
|
:param Dictionary attrs:
|
||||||
|
A dictionary with all attributes
|
||||||
|
:return:
|
||||||
|
A FakeResource object, with uuid and other attributes
|
||||||
|
"""
|
||||||
|
attrs = attrs or {}
|
||||||
|
|
||||||
|
# Set default attribute
|
||||||
|
instance_type_info = {
|
||||||
|
"created_at": "2016-09-27T02:37:21.966342+00:00",
|
||||||
|
"description": "fake_description",
|
||||||
|
"extra_specs": {},
|
||||||
|
"is_public": True,
|
||||||
|
"name": "instance-type-name-" + uuid.uuid4().hex,
|
||||||
|
"updated_at": None,
|
||||||
|
"uuid": "instance-type-id-" + uuid.uuid4().hex,
|
||||||
|
}
|
||||||
|
|
||||||
|
# Overwrite default attributes.
|
||||||
|
instance_type_info.update(attrs)
|
||||||
|
|
||||||
|
instance_type = FakeResource(
|
||||||
|
manager=None,
|
||||||
|
info=copy.deepcopy(instance_type_info),
|
||||||
|
loaded=True)
|
||||||
|
return instance_type
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def create_instance_types(attrs=None, count=2):
|
||||||
|
"""Create multiple fake instance types.
|
||||||
|
|
||||||
|
:param Dictionary attrs:
|
||||||
|
A dictionary with all attributes
|
||||||
|
:param int count:
|
||||||
|
The number of instance types to fake
|
||||||
|
:return:
|
||||||
|
A list of FakeResource objects faking the instance types
|
||||||
|
"""
|
||||||
|
instance_types = []
|
||||||
|
for i in range(0, count):
|
||||||
|
instance_types.append(
|
||||||
|
FakeInstanceType.create_one_instance_type(attrs))
|
||||||
|
|
||||||
|
return instance_types
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def get_instance_types(instance_types=None, count=2):
|
||||||
|
"""Get an iterable Mock object with a list of faked instance types.
|
||||||
|
|
||||||
|
If instance_types list is provided, then initialize the Mock object
|
||||||
|
with the list. Otherwise create one.
|
||||||
|
|
||||||
|
:param List instance_types:
|
||||||
|
A list of FakeResource objects faking instance types
|
||||||
|
:param int count:
|
||||||
|
The number of instance types to fake
|
||||||
|
:return:
|
||||||
|
An iterable Mock object with side_effect set to a list of faked
|
||||||
|
instance types
|
||||||
|
"""
|
||||||
|
if instance_types is None:
|
||||||
|
instance_types = FakeInstanceType.create_instance_types(count)
|
||||||
|
return mock.Mock(side_effect=instance_types)
|
||||||
|
|||||||
0
nimbleclient/tests/unit/osc/v1/__init__.py
Normal file
0
nimbleclient/tests/unit/osc/v1/__init__.py
Normal file
263
nimbleclient/tests/unit/osc/v1/test_instance_type.py
Normal file
263
nimbleclient/tests/unit/osc/v1/test_instance_type.py
Normal file
@@ -0,0 +1,263 @@
|
|||||||
|
# Copyright 2016 Huawei, Inc. 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 mock
|
||||||
|
|
||||||
|
from osc_lib import utils
|
||||||
|
|
||||||
|
from nimbleclient.common import base
|
||||||
|
from nimbleclient.osc.v1 import instance_type
|
||||||
|
from nimbleclient.tests.unit import base as test_base
|
||||||
|
from nimbleclient.tests.unit import fakes
|
||||||
|
from nimbleclient.v1 import instance_type as instance_type_mgr
|
||||||
|
|
||||||
|
|
||||||
|
class TestInstanceType(test_base.TestBaremetalComputeV1):
|
||||||
|
fake_type = fakes.FakeInstanceType.create_one_instance_type()
|
||||||
|
|
||||||
|
columns = (
|
||||||
|
'created_at',
|
||||||
|
'description',
|
||||||
|
'extra_specs',
|
||||||
|
'is_public',
|
||||||
|
'name',
|
||||||
|
'updated_at',
|
||||||
|
'uuid',
|
||||||
|
)
|
||||||
|
|
||||||
|
data = (
|
||||||
|
fake_type.created_at,
|
||||||
|
fake_type.description,
|
||||||
|
fake_type.extra_specs,
|
||||||
|
fake_type.is_public,
|
||||||
|
fake_type.name,
|
||||||
|
fake_type.updated_at,
|
||||||
|
fake_type.uuid,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@mock.patch.object(instance_type_mgr.InstanceTypeManager, '_create')
|
||||||
|
class TestInstanceTypeCreate(TestInstanceType):
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
super(TestInstanceTypeCreate, self).setUp()
|
||||||
|
self.cmd = instance_type.CreateType(self.app, None)
|
||||||
|
|
||||||
|
def test_type_create(self, mock_create):
|
||||||
|
arglist = [
|
||||||
|
'type1',
|
||||||
|
]
|
||||||
|
verifylist = [
|
||||||
|
('name', 'type1'),
|
||||||
|
]
|
||||||
|
mock_create.return_value = self.fake_type
|
||||||
|
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
|
||||||
|
columns, data = self.cmd.take_action(parsed_args)
|
||||||
|
mock_create.assert_called_once_with('/types',
|
||||||
|
data={
|
||||||
|
'name': 'type1',
|
||||||
|
'is_public': True,
|
||||||
|
'description': None,
|
||||||
|
})
|
||||||
|
self.assertEqual(self.columns, columns)
|
||||||
|
self.assertEqual(self.data, data)
|
||||||
|
|
||||||
|
def test_type_create_with_public(self, mock_create):
|
||||||
|
arglist = [
|
||||||
|
'--public',
|
||||||
|
'type1',
|
||||||
|
]
|
||||||
|
verifylist = [
|
||||||
|
('public', True),
|
||||||
|
('name', 'type1'),
|
||||||
|
]
|
||||||
|
mock_create.return_value = self.fake_type
|
||||||
|
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
|
||||||
|
columns, data = self.cmd.take_action(parsed_args)
|
||||||
|
mock_create.assert_called_once_with('/types',
|
||||||
|
data={
|
||||||
|
'name': 'type1',
|
||||||
|
'is_public': True,
|
||||||
|
'description': None,
|
||||||
|
})
|
||||||
|
self.assertEqual(self.columns, columns)
|
||||||
|
self.assertEqual(self.data, data)
|
||||||
|
|
||||||
|
def test_type_create_with_private(self, mock_create):
|
||||||
|
arglist = [
|
||||||
|
'--private',
|
||||||
|
'type1',
|
||||||
|
]
|
||||||
|
verifylist = [
|
||||||
|
('private', True),
|
||||||
|
('name', 'type1'),
|
||||||
|
]
|
||||||
|
mock_create.return_value = self.fake_type
|
||||||
|
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
|
||||||
|
columns, data = self.cmd.take_action(parsed_args)
|
||||||
|
mock_create.assert_called_once_with('/types',
|
||||||
|
data={
|
||||||
|
'name': 'type1',
|
||||||
|
'is_public': False,
|
||||||
|
'description': None,
|
||||||
|
})
|
||||||
|
self.assertEqual(self.columns, columns)
|
||||||
|
self.assertEqual(self.data, data)
|
||||||
|
|
||||||
|
def test_type_create_with_description(self, mock_create):
|
||||||
|
arglist = [
|
||||||
|
'--description', 'test description.',
|
||||||
|
'type1',
|
||||||
|
]
|
||||||
|
verifylist = [
|
||||||
|
('description', 'test description.'),
|
||||||
|
('name', 'type1'),
|
||||||
|
]
|
||||||
|
mock_create.return_value = self.fake_type
|
||||||
|
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
|
||||||
|
columns, data = self.cmd.take_action(parsed_args)
|
||||||
|
mock_create.assert_called_once_with(
|
||||||
|
'/types',
|
||||||
|
data={
|
||||||
|
'name': 'type1',
|
||||||
|
'is_public': True,
|
||||||
|
'description': 'test description.',
|
||||||
|
})
|
||||||
|
self.assertEqual(self.columns, columns)
|
||||||
|
self.assertEqual(self.data, data)
|
||||||
|
|
||||||
|
|
||||||
|
@mock.patch.object(utils, 'find_resource')
|
||||||
|
@mock.patch.object(instance_type_mgr.InstanceTypeManager, '_delete')
|
||||||
|
class TestInstanceTypeDelete(TestInstanceType):
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
super(TestInstanceTypeDelete, self).setUp()
|
||||||
|
self.cmd = instance_type.DeleteType(self.app, None)
|
||||||
|
|
||||||
|
def test_type_delete(self, mock_delete, mock_find):
|
||||||
|
arglist = [
|
||||||
|
'type1',
|
||||||
|
]
|
||||||
|
verifylist = [
|
||||||
|
('type', ['type1']),
|
||||||
|
]
|
||||||
|
mock_find.return_value = self.fake_type
|
||||||
|
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
|
||||||
|
result = self.cmd.take_action(parsed_args)
|
||||||
|
expected_url = '/types/%s' % base.getid(self.fake_type)
|
||||||
|
mock_delete.assert_called_once_with(expected_url)
|
||||||
|
self.assertIsNone(result)
|
||||||
|
|
||||||
|
def test_type_multiple_delete(self, mock_delete, mock_find):
|
||||||
|
arglist = [
|
||||||
|
'type1',
|
||||||
|
'type2',
|
||||||
|
'type3'
|
||||||
|
]
|
||||||
|
verifylist = [
|
||||||
|
('type', ['type1', 'type2', 'type3']),
|
||||||
|
]
|
||||||
|
mock_find.return_value = self.fake_type
|
||||||
|
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
|
||||||
|
result = self.cmd.take_action(parsed_args)
|
||||||
|
expected_url = '/types/%s' % base.getid(self.fake_type)
|
||||||
|
expected_call = [mock.call(expected_url), mock.call(expected_url),
|
||||||
|
mock.call(expected_url)]
|
||||||
|
mock_delete.assert_has_calls(expected_call)
|
||||||
|
self.assertIsNone(result)
|
||||||
|
|
||||||
|
|
||||||
|
@mock.patch.object(instance_type_mgr.InstanceTypeManager, '_list')
|
||||||
|
class TestInstanceTypeList(TestInstanceType):
|
||||||
|
|
||||||
|
list_columns = (
|
||||||
|
"UUID",
|
||||||
|
"Name",
|
||||||
|
"Is Public",
|
||||||
|
)
|
||||||
|
|
||||||
|
list_columns_long = (
|
||||||
|
"UUID",
|
||||||
|
"Name",
|
||||||
|
"Is Public",
|
||||||
|
"Description",
|
||||||
|
"Properties",
|
||||||
|
)
|
||||||
|
|
||||||
|
list_data = ((
|
||||||
|
TestInstanceType.fake_type.uuid,
|
||||||
|
TestInstanceType.fake_type.name,
|
||||||
|
TestInstanceType.fake_type.is_public,
|
||||||
|
), )
|
||||||
|
|
||||||
|
list_data_long = ((
|
||||||
|
TestInstanceType.fake_type.uuid,
|
||||||
|
TestInstanceType.fake_type.name,
|
||||||
|
TestInstanceType.fake_type.is_public,
|
||||||
|
TestInstanceType.fake_type.description,
|
||||||
|
TestInstanceType.fake_type.extra_specs,
|
||||||
|
), )
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
super(TestInstanceTypeList, self).setUp()
|
||||||
|
self.cmd = instance_type.ListType(self.app, None)
|
||||||
|
|
||||||
|
def test_type_list(self, mock_list):
|
||||||
|
arglist = []
|
||||||
|
verifylist = []
|
||||||
|
mock_list.return_value = [self.fake_type]
|
||||||
|
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
|
||||||
|
columns, data = self.cmd.take_action(parsed_args)
|
||||||
|
mock_list.assert_called_once_with('/types', response_key='types')
|
||||||
|
self.assertEqual(self.list_columns, columns)
|
||||||
|
self.assertEqual(self.list_data, tuple(data))
|
||||||
|
|
||||||
|
def test_type_list_with_long(self, mock_list):
|
||||||
|
arglist = [
|
||||||
|
'--long',
|
||||||
|
]
|
||||||
|
verifylist = [
|
||||||
|
('long', True),
|
||||||
|
]
|
||||||
|
mock_list.return_value = [self.fake_type]
|
||||||
|
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
|
||||||
|
columns, data = self.cmd.take_action(parsed_args)
|
||||||
|
mock_list.assert_called_once_with('/types', response_key='types')
|
||||||
|
self.assertEqual(self.list_columns_long, columns)
|
||||||
|
self.assertEqual(self.list_data_long, tuple(data))
|
||||||
|
|
||||||
|
|
||||||
|
@mock.patch.object(instance_type_mgr.InstanceTypeManager, '_get')
|
||||||
|
class TestInstanceTypeShow(TestInstanceType):
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
super(TestInstanceTypeShow, self).setUp()
|
||||||
|
self.cmd = instance_type.ShowType(self.app, None)
|
||||||
|
|
||||||
|
def test_type_show(self, mock_get):
|
||||||
|
arglist = [
|
||||||
|
'type1',
|
||||||
|
]
|
||||||
|
verifylist = [
|
||||||
|
('type', 'type1'),
|
||||||
|
]
|
||||||
|
mock_get.return_value = self.fake_type
|
||||||
|
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
|
||||||
|
columns, data = self.cmd.take_action(parsed_args)
|
||||||
|
expected_url = '/types/%s' % parsed_args.type
|
||||||
|
mock_get.assert_called_once_with(expected_url)
|
||||||
|
self.assertEqual(self.columns, columns)
|
||||||
|
self.assertEqual(self.data, data)
|
||||||
@@ -14,6 +14,7 @@
|
|||||||
#
|
#
|
||||||
|
|
||||||
from nimbleclient.common import http
|
from nimbleclient.common import http
|
||||||
|
from nimbleclient.v1 import instance_type
|
||||||
|
|
||||||
|
|
||||||
class Client(object):
|
class Client(object):
|
||||||
@@ -22,3 +23,7 @@ class Client(object):
|
|||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
"""Initialize a new client for the Nimble v1 API."""
|
"""Initialize a new client for the Nimble v1 API."""
|
||||||
self.http_client = http._construct_http_client(*args, **kwargs)
|
self.http_client = http._construct_http_client(*args, **kwargs)
|
||||||
|
|
||||||
|
self.instance_type = instance_type.InstanceTypeManager(
|
||||||
|
self.http_client)
|
||||||
|
self.instance = None
|
||||||
|
|||||||
45
nimbleclient/v1/instance_type.py
Normal file
45
nimbleclient/v1/instance_type.py
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
# Copyright 2016 Huawei, Inc. 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 nimbleclient.common import base
|
||||||
|
|
||||||
|
|
||||||
|
class InstanceType(base.Resource):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class InstanceTypeManager(base.ManagerWithFind):
|
||||||
|
resource_class = InstanceType
|
||||||
|
|
||||||
|
def create(self, name, is_public, description=None):
|
||||||
|
url = '/types'
|
||||||
|
data = {
|
||||||
|
'name': name,
|
||||||
|
'is_public': is_public,
|
||||||
|
'description': description,
|
||||||
|
}
|
||||||
|
return self._create(url, data=data)
|
||||||
|
|
||||||
|
def delete(self, instance_type):
|
||||||
|
url = '/types/%s' % base.getid(instance_type)
|
||||||
|
return self._delete(url)
|
||||||
|
|
||||||
|
def get(self, instance_type):
|
||||||
|
url = '/types/%s' % base.getid(instance_type)
|
||||||
|
return self._get(url)
|
||||||
|
|
||||||
|
def list(self):
|
||||||
|
url = '/types'
|
||||||
|
return self._list(url, response_key='types')
|
||||||
@@ -28,6 +28,10 @@ openstack.cli.extension =
|
|||||||
baremetal_compute = nimbleclient.osc.plugin
|
baremetal_compute = nimbleclient.osc.plugin
|
||||||
|
|
||||||
openstack.baremetal_compute.v1 =
|
openstack.baremetal_compute.v1 =
|
||||||
|
baremetal_compute_type_create = nimbleclient.osc.v1.instance_type:CreateType
|
||||||
|
baremetal_compute_type_delete = nimbleclient.osc.v1.instance_type:DeleteType
|
||||||
|
baremetal_compute_type_list = nimbleclient.osc.v1.instance_type:ListType
|
||||||
|
baremetal_compute_type_show = nimbleclient.osc.v1.instance_type:ShowType
|
||||||
|
|
||||||
|
|
||||||
[build_sphinx]
|
[build_sphinx]
|
||||||
@@ -55,4 +59,4 @@ output_file = nimbleclient/locale/nimbleclient.pot
|
|||||||
[build_releasenotes]
|
[build_releasenotes]
|
||||||
all_files = 1
|
all_files = 1
|
||||||
build-dir = releasenotes/build
|
build-dir = releasenotes/build
|
||||||
source-dir = releasenotes/source
|
source-dir = releasenotes/source
|
||||||
|
|||||||
Reference in New Issue
Block a user