Merge "Add fuel2 commands to operate on extensions"
This commit is contained in:
commit
253d7ebc25
|
@ -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,
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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)
|
|
@ -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
|
||||
|
|
|
@ -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)
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
|
@ -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)
|
|
@ -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
|
||||
|
|
|
@ -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)]
|
|
@ -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',
|
||||
|
|
|
@ -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)
|
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue