Merge "Add fuel2 commands to operate on extensions"

This commit is contained in:
Jenkins 2016-09-14 16:53:25 +00:00 committed by Gerrit Code Review
commit 253d7ebc25
13 changed files with 430 additions and 3 deletions

View File

@ -64,6 +64,7 @@ def get_client(resource, version='v1', connection=None):
'deployment_history': v1.deployment_history,
'deployment-info': v1.deployment_info,
'environment': v1.environment,
'extension': v1.extension,
'fuel-version': v1.fuelversion,
'graph': v1.graph,
'network-configuration': v1.network_configuration,

View File

@ -78,6 +78,10 @@ class BaseListCommand(lister.Lister, BaseCommand):
filters = {}
@property
def default_sorting_by(self):
return ['id']
@abc.abstractproperty
def columns(self):
"""Names of columns in the resulting table."""
@ -100,10 +104,11 @@ class BaseListCommand(lister.Lister, BaseCommand):
nargs='+',
choices=self.columns,
metavar='SORT_COLUMN',
default=['id'],
default=self.default_sorting_by,
help='Space separated list of keys for sorting '
'the data. Defaults to id. Wrong values '
'are ignored.')
'the data. Defaults to {}. Wrong values '
'are ignored.'.format(
', '.join(self.default_sorting_by)))
return parser

View File

@ -0,0 +1,98 @@
# -*- 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 cliff import show
from fuelclient.commands import base
class ExtensionMixIn(object):
entity_name = 'extension'
class ExtensionList(ExtensionMixIn, base.BaseListCommand):
"""Show list of all available extensions."""
columns = ("name",
"version",
"description",
"provides")
default_sorting_by = ["name"]
class EnvExtensionShow(ExtensionMixIn, base.BaseShowCommand):
"""Show list of enabled extensions for environment with given id."""
columns = ("extensions", )
def get_parser(self, prog_name):
# Avoid adding id argument by BaseShowCommand
# Because it adds 'id' with wrong help message for this class
parser = show.ShowOne.get_parser(self, prog_name)
parser.add_argument('id', type=int, help='Id of the environment.')
return parser
class EnvExtensionEnable(ExtensionMixIn, base.BaseCommand):
"""Enable specified extensions for environment with given id."""
def get_parser(self, prog_name):
parser = super(EnvExtensionEnable, self).get_parser(prog_name)
parser.add_argument('id', type=int, help='Id of the environment.')
parser.add_argument('-E',
'--extensions',
required=True,
nargs='+',
help='Names of extensions to enable.')
return parser
def take_action(self, parsed_args):
self.client.enable_extensions(parsed_args.id, parsed_args.extensions)
msg = ('The following extensions: {e} have been enabled for '
'the environment with id {id}.\n'.format(
e=', '.join(parsed_args.extensions), id=parsed_args.id))
self.app.stdout.write(msg)
class EnvExtensionDisable(ExtensionMixIn, base.BaseCommand):
"""Disable specified extensions for environment with given id."""
def get_parser(self, prog_name):
parser = super(EnvExtensionDisable, self).get_parser(prog_name)
parser.add_argument('id', type=int, help='Id of the environment.')
parser.add_argument('-E',
'--extensions',
required=True,
nargs='+',
help='Names of extensions to disable.')
return parser
def take_action(self, parsed_args):
self.client.disable_extensions(parsed_args.id, parsed_args.extensions)
msg = ('The following extensions: {e} have been disabled for '
'the environment with id {id}.\n'.format(
e=', '.join(parsed_args.extensions), id=parsed_args.id))
self.app.stdout.write(msg)

View File

@ -18,6 +18,7 @@ functionality from nailgun objects.
from fuelclient.objects.base import BaseObject
from fuelclient.objects.environment import Environment
from fuelclient.objects.extension import Extension
from fuelclient.objects.node import Node
from fuelclient.objects.node import NodeCollection
from fuelclient.objects.openstack_config import OpenstackConfig

View File

@ -0,0 +1,47 @@
# 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.objects.base import BaseObject
class Extension(BaseObject):
class_api_path = "extensions/"
instance_api_path = "clusters/{0}/extensions/"
@property
def extensions_url(self):
return self.instance_api_path.format(self.id)
def get_env_extensions(self):
"""Get list of extensions through request to the Nailgun API
"""
return self.connection.get_request(self.extensions_url)
def enable_env_extensions(self, extensions):
"""Enable extensions through request to the Nailgun API
:param extensions: list of extenstion to be enabled
"""
return self.connection.put_request(self.extensions_url, extensions)
def disable_env_extensions(self, extensions):
"""Disable extensions through request to the Nailgun API
:param extensions: list of extenstion to be disabled
"""
url = '{0}?extension_names={1}'.format(self.extensions_url,
','.join(extensions))
return self.connection.delete_request(url)

View File

@ -49,3 +49,34 @@ class TestDeployChanges(base.CLIv2TestCase):
self.check_for_stdout_by_regexp(self.cmd_redeploy_changes,
self.pattern_success)
class TestExtensionManagement(base.CLIv2TestCase):
cmd_create_env = "env create -r {0} cluster-test-extensions-mgmt"
cmd_disable_exts = "env extension disable 1 --extensions volume_manager"
cmd_enable_exts = "env extension enable 1 --extensions volume_manager"
pattern_enable_success = (r"^The following extensions: volume_manager "
r"have been enabled for the environment with "
r"id 1.\n$")
pattern_disable_success = (r"^The following extensions: volume_manager "
r"have been disabled for the environment with "
r"id 1.\n$")
def setUp(self):
super(TestExtensionManagement, self).setUp()
self.load_data_to_nailgun_server()
release_id = self.get_first_deployable_release_id()
self.cmd_create_env = self.cmd_create_env.format(release_id)
self.run_cli_commands((
self.cmd_create_env,
))
def test_disable_extensions(self):
self.check_for_stdout_by_regexp(self.cmd_disable_exts,
self.pattern_disable_success)
def test_enable_extensions(self):
self.check_for_stdout_by_regexp(self.cmd_enable_exts,
self.pattern_enable_success)

View File

@ -0,0 +1,79 @@
# -*- 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.
import mock
from fuelclient.tests.unit.v2.cli import test_engine
from fuelclient.tests import utils
class TestExtensionCommand(test_engine.BaseCLITest):
"""Tests for fuel2 extension * commands."""
def test_extensions_list(self):
self.m_client.get_all.return_value = utils.get_fake_extensions(2)
args = 'extension list'
self.exec_command(args)
self.m_client.get_all.assert_called_once_with()
self.m_get_client.assert_called_once_with('extension', mock.ANY)
def test_env_extensions_show(self):
self.m_client.get_extensions.return_value = \
utils.get_fake_env_extensions()
env_id = 45
args = 'env extension show {id}'.format(id=env_id)
self.exec_command(args)
self.m_client.get_by_id.assert_called_once_with(env_id)
self.m_get_client.assert_called_once_with('extension', mock.ANY)
@mock.patch('sys.stderr')
def test_env_extension_show_fail(self, mocked_stderr):
args = 'env extension show'
self.assertRaises(SystemExit, self.exec_command, args)
self.assertIn('id',
mocked_stderr.write.call_args_list[0][0][0])
@mock.patch('sys.stderr')
def test_env_extension_enable_fail(self, mocked_stderr):
args = 'env extension enable 1'
self.assertRaises(SystemExit, self.exec_command, args)
self.assertIn('-E/--extensions',
mocked_stderr.write.call_args_list[-1][0][0])
@mock.patch('sys.stderr')
def test_env_extension_disable_fail(self, mocked_stderr):
args = 'env extension disable 1'
self.assertRaises(SystemExit, self.exec_command, args)
self.assertIn('-E/--extensions',
mocked_stderr.write.call_args_list[-1][0][0])
def test_env_extensions_enable(self):
exts = utils.get_fake_env_extensions()
env_id = 45
args = 'env extension enable {id} --extensions {exts}'.format(
id=env_id, exts=' '.join(exts))
self.exec_command(args)
self.m_client.enable_extensions.assert_called_once_with(env_id, exts)
self.m_get_client.assert_called_once_with('extension', mock.ANY)
def test_env_extensions_disable(self):
exts = utils.get_fake_env_extensions()
env_id = 45
args = 'env extension disable {id} --extensions {exts}'.format(
id=env_id, exts=' '.join(exts))
self.exec_command(args)
self.m_client.disable_extensions.assert_called_once_with(env_id, exts)
self.m_get_client.assert_called_once_with('extension', mock.ANY)

View File

@ -0,0 +1,83 @@
# -*- 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.
import fuelclient
from fuelclient.tests.unit.v2.lib import test_api
from fuelclient.tests import utils
class TestExtensionFacade(test_api.BaseLibTest):
def setUp(self):
super(TestExtensionFacade, self).setUp()
self.version = 'v1'
self.res_uri = '/api/{version}/extensions/'.format(
version=self.version)
self.res_env_uri = '/api/{version}/clusters/'.format(
version=self.version)
self.fake_ext = ['fake_ext1']
self.fake_extensions = utils.get_fake_extensions(10)
self.fake_env_extensions = utils.get_fake_env_extensions()
self.client = fuelclient.get_client('extension', self.version)
def test_extension_list(self):
matcher = self.m_request.get(self.res_uri, json=self.fake_extensions)
self.client.get_all()
self.assertTrue(matcher.called)
def test_env_extension_list(self):
env_id = 42
expected_uri = self.get_object_uri(self.res_env_uri, env_id,
'/extensions/')
matcher = self.m_request.get(expected_uri,
json=self.fake_env_extensions)
extensions = self.client.get_by_id(env_id)
self.assertTrue(matcher.called)
for ext in self.fake_env_extensions:
self.assertIn(ext, extensions['extensions'])
def test_env_extension_enable(self):
env_id = 42
fake_ext = ['enabled_fake_ext4']
expected_uri = self.get_object_uri(self.res_env_uri, env_id,
'/extensions/')
put_matcher = self.m_request.put(expected_uri,
json=self.fake_env_extensions)
self.client.enable_extensions(env_id, fake_ext)
self.assertTrue(put_matcher.called)
self.assertIn(fake_ext[0], put_matcher.last_request.json())
def test_env_extension_disable(self):
env_id = 42
expected_uri = self.get_object_uri(
self.res_env_uri,
env_id,
'/extensions/?extension_names={0}'.format(
','.join(self.fake_env_extensions)))
delete_matcher = self.m_request.delete(expected_uri,
complete_qs=True,
json=self.fake_env_extensions)
self.client.disable_extensions(env_id, self.fake_env_extensions)
self.assertTrue(delete_matcher.called)

View File

@ -32,6 +32,9 @@ from fuelclient.tests.utils.fake_net_conf import get_fake_network_config
from fuelclient.tests.utils.fake_network_group import get_fake_network_group
from fuelclient.tests.utils.fake_node import get_fake_node
from fuelclient.tests.utils.fake_env import get_fake_env
from fuelclient.tests.utils.fake_extension import get_fake_env_extensions
from fuelclient.tests.utils.fake_extension import get_fake_extension
from fuelclient.tests.utils.fake_extension import get_fake_extensions
from fuelclient.tests.utils.fake_fuel_version import get_fake_fuel_version
from fuelclient.tests.utils.fake_task import get_fake_task
from fuelclient.tests.utils.fake_node_group import get_fake_node_group

View File

@ -0,0 +1,36 @@
# -*- 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.
def get_fake_env_extensions(names=None):
"""Create a list of fake extensions for particular env"""
return names or ['fake_ext1', 'fake_ext2', 'fake_ext3']
def get_fake_extension(name=None, version=None, provides=None,
description=None):
return {'name': name or 'fake_name',
'version': version or 'fake_version',
'provides': provides or ['fake_method_call'],
'description': description or 'fake_description',
}
def get_fake_extensions(extension_count, **kwargs):
"""Create a random fake list of extensions."""
return [get_fake_extension(**kwargs)
for _ in range(extension_count)]

View File

@ -16,6 +16,7 @@ from fuelclient.v1 import cluster_settings
from fuelclient.v1 import deployment_history
from fuelclient.v1 import deployment_info
from fuelclient.v1 import environment
from fuelclient.v1 import extension
from fuelclient.v1 import fuelversion
from fuelclient.v1 import graph
from fuelclient.v1 import network_configuration
@ -35,6 +36,7 @@ __all__ = ('cluster_settings',
'deployment_history',
'deployment_info',
'environment',
'extension',
'fuelversion',
'graph',
'network_configuration',

View File

@ -0,0 +1,37 @@
# 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 import objects
from fuelclient.v1 import base_v1
class ExtensionClient(base_v1.BaseV1Client):
_entity_wrapper = objects.Extension
def get_by_id(self, environment_id):
ext_obj = self._entity_wrapper(environment_id)
return {'extensions': ', '.join(ext_obj.get_env_extensions())}
def enable_extensions(self, environment_id, extensions):
ext_obj = self._entity_wrapper(environment_id)
return ext_obj.enable_env_extensions(extensions)
def disable_extensions(self, environment_id, extensions):
ext_obj = self._entity_wrapper(environment_id)
return ext_obj.disable_env_extensions(extensions)
def get_client(connection):
return ExtensionClient(connection)

View File

@ -37,6 +37,9 @@ fuelclient =
env_deployment-facts_download=fuelclient.commands.environment:EnvDeploymentFactsDownload
env_deployment-facts_get-default=fuelclient.commands.environment:EnvDeploymentFactsGetDefault
env_deployment-facts_upload=fuelclient.commands.environment:EnvDeploymentFactsUpload
env_extension_disable=fuelclient.commands.extension:EnvExtensionDisable
env_extension_enable=fuelclient.commands.extension:EnvExtensionEnable
env_extension_show=fuelclient.commands.extension:EnvExtensionShow
env_list=fuelclient.commands.environment:EnvList
env_network_download=fuelclient.commands.environment:EnvNetworkDownload
env_network_upload=fuelclient.commands.environment:EnvNetworkUpload
@ -56,6 +59,7 @@ fuelclient =
env_spawn-vms=fuelclient.commands.environment:EnvSpawnVms
env_stop-deployment=fuelclient.commands.environment:EnvStopDeploy
env_update=fuelclient.commands.environment:EnvUpdate
extension_list=fuelclient.commands.extension:ExtensionList
fuel-version=fuelclient.commands.fuelversion:FuelVersion
graph_download=fuelclient.commands.graph:GraphDownload
graph_execute=fuelclient.commands.graph:GraphExecute