Add 'fuel2 release component list' command
In Nailgun there is an API call for getting JSON data of compatible components for release and all plugins releated with it (GET /api/v1/releases/<:id>/components/). This patch adds new fuel2 command, e.g.: fuel2 release component list 1 This command prints list of components for given Fuel release with respective cross-dependencies DocImpact Closes-Bug: 1600501 Change-Id: I055d6fb4dde4cb0ac9508084155bbce5a106220c
This commit is contained in:
parent
962c5bff48
commit
287e5a2470
|
@ -79,3 +79,56 @@ class ReleaseReposUpdate(ReleaseMixIn, base.BaseCommand):
|
|||
file=parsed_args.file
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
class ReleaseComponentList(ReleaseMixIn, base.BaseListCommand):
|
||||
"""Show list of components for a given release."""
|
||||
|
||||
columns = ("name",
|
||||
"requires",
|
||||
"compatible",
|
||||
"incompatible",
|
||||
"default")
|
||||
|
||||
@staticmethod
|
||||
def retrieve_predicates(statement):
|
||||
"""Retrieve predicates with respective 'items' components
|
||||
|
||||
:param statement: the dictionary to extract predicate values from
|
||||
:return: retrieval result as a string
|
||||
"""
|
||||
predicates = ('any_of', 'all_of', 'one_of', 'none_of')
|
||||
for predicate in predicates:
|
||||
if predicate in statement:
|
||||
result = ', '.join(statement[predicate].get('items'))
|
||||
return "{0} ({1})".format(predicate, result)
|
||||
raise ValueError('Predicates not found.')
|
||||
|
||||
@classmethod
|
||||
def retrieve_data(cls, value):
|
||||
"""Retrieve names of components or predicates from nested data
|
||||
|
||||
:param value: data to extract name or to retrieve predicates from
|
||||
:return: names of components or predicates as a string
|
||||
"""
|
||||
if isinstance(value, list):
|
||||
# get only "name" of components otherwise retrieve predicates
|
||||
return ', '.join([v['name'] if 'name' in v
|
||||
else cls.retrieve_predicates(v)
|
||||
for v in value])
|
||||
return value
|
||||
|
||||
def get_parser(self, prog_name):
|
||||
parser = super(ReleaseComponentList, self).get_parser(prog_name)
|
||||
parser.add_argument('id', type=int,
|
||||
help='Id of the {0}.'.format(self.entity_name))
|
||||
return parser
|
||||
|
||||
def take_action(self, parsed_args):
|
||||
data = self.client.get_components_by_id(parsed_args.id)
|
||||
# some keys (columns) can be missed in origin data
|
||||
# then create them with respective '-' value
|
||||
data = [{k: self.retrieve_data(d.get(k, '-')) for k in self.columns}
|
||||
for d in data]
|
||||
data = data_utils.get_display_data_multi(self.columns, data)
|
||||
return self.columns, data
|
||||
|
|
|
@ -22,6 +22,7 @@ class Release(BaseObject):
|
|||
networks_path = 'releases/{0}/networks'
|
||||
attributes_metadata_path = 'releases/{0}/attributes_metadata'
|
||||
deployment_tasks_path = 'releases/{0}/deployment_tasks'
|
||||
components_path = 'releases/{0}/components'
|
||||
|
||||
def get_networks(self):
|
||||
url = self.networks_path.format(self.id)
|
||||
|
@ -46,3 +47,7 @@ class Release(BaseObject):
|
|||
def update_deployment_tasks(self, data):
|
||||
url = self.deployment_tasks_path.format(self.id)
|
||||
return self.connection.put_request(url, data)
|
||||
|
||||
def get_components(self):
|
||||
url = self.components_path.format(self.id)
|
||||
return self.connection.get_request(url)
|
||||
|
|
|
@ -0,0 +1,88 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright 2016 Mirantis, Inc.
|
||||
#
|
||||
# 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 fuelclient.commands.release import ReleaseComponentList
|
||||
from fuelclient.tests.unit.v1 import base
|
||||
|
||||
|
||||
class TestReleaseComponent(base.UnitTestCase):
|
||||
|
||||
def test_retrieve_predicates(self):
|
||||
predicates = ('any_of', 'all_of', 'one_of', 'none_of')
|
||||
items = {
|
||||
"items": ["fake:component:1",
|
||||
"fake:component:2"]
|
||||
}
|
||||
|
||||
for predicate in predicates:
|
||||
test_data = {predicate: items}
|
||||
real_data = ReleaseComponentList.retrieve_predicates(test_data)
|
||||
expected_data = "{} (fake:component:1, fake:component:2)".format(
|
||||
predicate)
|
||||
self.assertEqual(expected_data, real_data)
|
||||
|
||||
def test_retrieve_predicates_w_wrong_predicate(self):
|
||||
test_data = {
|
||||
"bad_predicate": {
|
||||
"items": ["fake:component:1",
|
||||
"fake:component:2"],
|
||||
}
|
||||
}
|
||||
|
||||
self.assertRaisesRegexp(ValueError,
|
||||
"Predicates not found.",
|
||||
ReleaseComponentList.retrieve_predicates,
|
||||
test_data)
|
||||
|
||||
def test_retrieve_data(self):
|
||||
test_data = "fake:component:1"
|
||||
real_data = ReleaseComponentList.retrieve_data(test_data)
|
||||
self.assertEqual("fake:component:1", real_data)
|
||||
|
||||
test_data = [{"name": "fake:component:1"}]
|
||||
real_data = ReleaseComponentList.retrieve_data(test_data)
|
||||
self.assertEqual("fake:component:1", real_data)
|
||||
|
||||
test_data = [
|
||||
{
|
||||
"one_of": {
|
||||
"items": ["fake:component:1"]
|
||||
}
|
||||
},
|
||||
{
|
||||
"any_of": {
|
||||
"items": ["fake:component:1",
|
||||
"fake:component:2"]
|
||||
}
|
||||
},
|
||||
{
|
||||
"all_of": {
|
||||
"items": ["fake:component:1",
|
||||
"fake:component:2"]
|
||||
}
|
||||
},
|
||||
{
|
||||
"none_of": {
|
||||
"items": ["fake:component:1"]
|
||||
}
|
||||
}
|
||||
]
|
||||
real_data = ReleaseComponentList.retrieve_data(test_data)
|
||||
expected_data = ("one_of (fake:component:1), "
|
||||
"any_of (fake:component:1, fake:component:2), "
|
||||
"all_of (fake:component:1, fake:component:2), "
|
||||
"none_of (fake:component:1)")
|
||||
self.assertEqual(expected_data, real_data)
|
|
@ -28,6 +28,8 @@ class TestReleaseCommand(test_engine.BaseCLITest):
|
|||
self.m_client.get_by_id.return_value = fake_release.get_fake_release()
|
||||
self.m_client.get_attributes_metadata_by_id.return_value = \
|
||||
fake_release.get_fake_attributes_metadata()
|
||||
self.m_client.get_components_by_id.return_value = \
|
||||
fake_release.get_fake_release_components(10)
|
||||
|
||||
def test_release_list(self):
|
||||
args = 'release list'
|
||||
|
@ -63,3 +65,10 @@ class TestReleaseCommand(test_engine.BaseCLITest):
|
|||
self.m_client.update_attributes_metadata_by_id \
|
||||
.assert_called_once_with(1, data)
|
||||
self.m_get_client.assert_called_once_with('release', mock.ANY)
|
||||
|
||||
def test_release_component_list(self):
|
||||
release_id = 42
|
||||
args = 'release component list {0}'.format(release_id)
|
||||
self.exec_command(args)
|
||||
self.m_client.get_components_by_id.assert_called_once_with(release_id)
|
||||
self.m_get_client.assert_called_once_with('release', mock.ANY)
|
||||
|
|
|
@ -29,6 +29,7 @@ class TestReleaseFacade(test_api.BaseLibTest):
|
|||
self.version = 'v1'
|
||||
self.res_uri = '/api/{version}/releases/'.format(version=self.version)
|
||||
self.fake_releases = utils.get_fake_releases(10)
|
||||
self.fake_release_components = utils.get_fake_release_components(10)
|
||||
self.fake_attributes_metadata = utils.get_fake_attributes_metadata()
|
||||
self.client = fuelclient.get_client('release', self.version)
|
||||
|
||||
|
@ -63,3 +64,12 @@ class TestReleaseFacade(test_api.BaseLibTest):
|
|||
self.assertTrue(m_put.called)
|
||||
self.assertEqual(m_put.last_request.json(),
|
||||
self.fake_attributes_metadata)
|
||||
|
||||
def test_release_component_list(self):
|
||||
release_id = 42
|
||||
expected_uri = self.get_object_uri(self.res_uri, release_id,
|
||||
'/components')
|
||||
matcher = self.m_request.get(expected_uri,
|
||||
json=self.fake_release_components)
|
||||
self.client.get_components_by_id(release_id)
|
||||
self.assertTrue(matcher.called)
|
||||
|
|
|
@ -41,6 +41,8 @@ from fuelclient.tests.utils.fake_plugin import get_fake_plugins
|
|||
from fuelclient.tests.utils.fake_release import get_fake_release
|
||||
from fuelclient.tests.utils.fake_release import get_fake_releases
|
||||
from fuelclient.tests.utils.fake_release import get_fake_attributes_metadata
|
||||
from fuelclient.tests.utils.fake_release import get_fake_release_component
|
||||
from fuelclient.tests.utils.fake_release import get_fake_release_components
|
||||
|
||||
|
||||
__all__ = (get_fake_deployment_history,
|
||||
|
@ -52,6 +54,8 @@ __all__ = (get_fake_deployment_history,
|
|||
get_fake_release,
|
||||
get_fake_releases,
|
||||
get_fake_attributes_metadata,
|
||||
get_fake_release_component,
|
||||
get_fake_release_components,
|
||||
get_fake_fuel_version,
|
||||
get_fake_interface_config,
|
||||
get_fake_network_group,
|
||||
|
|
|
@ -66,3 +66,64 @@ def get_fake_releases(release_count, **kwargs):
|
|||
"""Create a random fake release list."""
|
||||
return [get_fake_release(release_id=i, **kwargs)
|
||||
for i in range(1, release_count + 1)]
|
||||
|
||||
|
||||
def get_fake_release_component(name=None, requires=None, incompatible=None,
|
||||
compatible=None, default=None):
|
||||
"""Create a random fake component of release
|
||||
|
||||
Returns the serialized and parametrized representation of a dumped Fuel
|
||||
component of release. Represents the average amount of data.
|
||||
|
||||
"""
|
||||
return {
|
||||
'name': name or 'network:neutron:ml2:vlan',
|
||||
'description':
|
||||
'dialog.create_cluster_wizard.network.neutron_vlan_description',
|
||||
'weight': 5,
|
||||
'requires': requires or [
|
||||
{
|
||||
'one_of': {
|
||||
'items': ['hypervisor:qemu'],
|
||||
'message': 'dialog.create_cluster_wizard.compute.'
|
||||
'vcenter_warning'
|
||||
}
|
||||
},
|
||||
{
|
||||
'one_of': {
|
||||
'items': ['network:neutron:ml2:dvs',
|
||||
'network:neutron:ml2:nsx'],
|
||||
'message': 'dialog.create_cluster_wizard.compute.'
|
||||
'vcenter_requires_network_backend',
|
||||
'message_invalid': 'dialog.create_cluster_wizard.compute.'
|
||||
'vcenter_requires_network_plugins'
|
||||
}
|
||||
}
|
||||
],
|
||||
'incompatible': incompatible or [
|
||||
{'message': 'dialog.create_cluster_wizard.network.vlan_tun_alert',
|
||||
'name': 'network:neutron:ml2:tun'}
|
||||
],
|
||||
'compatible': compatible or [
|
||||
{'name': 'network:neutron:core:ml2'},
|
||||
{'name': 'hypervisor:qemu'},
|
||||
{'name': 'hypervisor:vmware'},
|
||||
{'name': 'storage:block:lvm'},
|
||||
{'name': 'storage:block:ceph'},
|
||||
{'name': 'storage:object:ceph'},
|
||||
{'name': 'storage:ephemeral:ceph'},
|
||||
{'name': 'storage:image:ceph'},
|
||||
{'name': 'additional_service:sahara'},
|
||||
{'name': 'additional_service:murano'},
|
||||
{'name': 'additional_service:ceilometer'},
|
||||
{'name': 'additional_service:ironic'}
|
||||
],
|
||||
'default': default,
|
||||
'label': 'common.network.neutron_vlan',
|
||||
}
|
||||
|
||||
|
||||
def get_fake_release_components(component_count, **kwargs):
|
||||
"""Create a random fake list of release components."""
|
||||
return [get_fake_release_component(**kwargs)
|
||||
for _ in range(component_count)]
|
||||
|
|
|
@ -28,6 +28,10 @@ class ReleaseClient(base_v1.BaseV1Client):
|
|||
release_obj = self._entity_wrapper(obj_id=release_id)
|
||||
return release_obj.get_attributes_metadata()
|
||||
|
||||
def get_components_by_id(self, release_id):
|
||||
release_obj = self._entity_wrapper(obj_id=release_id)
|
||||
return release_obj.get_components()
|
||||
|
||||
|
||||
def get_client(connection):
|
||||
return ReleaseClient(connection)
|
||||
|
|
|
@ -70,6 +70,7 @@ fuelclient =
|
|||
openstack-config_upload=fuelclient.commands.openstack_config:OpenstackConfigUpload
|
||||
plugins_list=fuelclient.commands.plugins:PluginsList
|
||||
plugins_sync=fuelclient.commands.plugins:PluginsSync
|
||||
release_component_list=fuelclient.commands.release:ReleaseComponentList
|
||||
release_list=fuelclient.commands.release:ReleaseList
|
||||
release_repos_list=fuelclient.commands.release:ReleaseReposList
|
||||
release_repos_update=fuelclient.commands.release:ReleaseReposUpdate
|
||||
|
|
Loading…
Reference in New Issue