Implement CLI v2 for openstack configuration

* Add openstack-config commands for fuel2
* Add API facade for openstack configuration

Examples for fuel2:

fuel2 openstack-config list --env 1
fuel2 openstack-config upload --env 1 [--node 1 | --role controller]
    --file config.yaml
fuel2 openstack-config download 1 --file config.yaml
fuel2 openstack-config execute --env 1 [--node 1 | --role controller]

Change-Id: I405d9e1f2d35406fe4c163a64b5cb7e2742b461b
Implements: blueprint openstack-config-change
This commit is contained in:
Alexander Saprykin
2015-11-18 16:58:37 +01:00
committed by Sergey Slipushenko
parent 88274ba134
commit 38f75fc6e8
6 changed files with 290 additions and 0 deletions

View File

@@ -52,6 +52,7 @@ def get_client(resource, version='v1'):
'fuel-version': v1.fuelversion,
'network-group': v1.network_group,
'node': v1.node,
'openstack-config': v1.openstack_config,
'plugins': v1.plugins,
'task': v1.task,
}

View File

@@ -0,0 +1,154 @@
# Copyright 2015 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 import base
from fuelclient.common import data_utils
class OpenstackConfigMixin(object):
entity_name = 'openstack-config'
@staticmethod
def add_env_arg(parser, required=False):
parser.add_argument(
'-e', '--env',
type=int, required=required,
help='Environment ID.')
@staticmethod
def add_file_arg(parser):
parser.add_argument(
'-f', '--file',
type=str, help='YAML file that contains openstack configuration.')
@staticmethod
def add_config_id_arg(parser):
parser.add_argument(
'config',
type=int, help='Openstack configuration ID.'
)
@staticmethod
def add_node_id_arg(parser):
parser.add_argument(
'-n', '--node',
type=int, default=None, help='Node ID.'
)
@staticmethod
def add_node_role_arg(parser):
parser.add_argument(
'-r', '--role',
type=str, default=None, help='Node role.'
)
@staticmethod
def add_deleted_arg(parser):
parser.add_argument(
'-D', '--deleted',
type=bool, default=False, help='Show deleted configurations.'
)
class OpenstackConfigList(OpenstackConfigMixin, base.BaseCommand):
"""List all openstack configurations.
"""
columns = (
'id', 'is_active', 'config_type',
'cluster_id', 'node_id', 'node_role')
def get_parser(self, prog_name):
parser = super(OpenstackConfigList, self).get_parser(prog_name)
self.add_env_arg(parser)
self.add_node_id_arg(parser)
self.add_node_role_arg(parser)
self.add_deleted_arg(parser)
return parser
def take_action(self, args):
data = self.client.get_filtered(
cluster_id=args.env, node_id=args.node,
node_role=args.role, is_active=(not args.deleted))
data = data_utils.get_display_data_multi(self.columns, data)
return self.columns, data
class OpenstackConfigDownload(OpenstackConfigMixin, base.BaseCommand):
"""Download specified configuration file.
"""
def get_parser(self, prog_name):
parser = super(OpenstackConfigDownload, self).get_parser(prog_name)
self.add_config_id_arg(parser)
self.add_file_arg(parser)
return parser
def take_action(self, args):
file_path = self.client.download(args.config, args.file)
msg = ("OpenStack configuration with id={0} "
"downloaded to {1}\n").format(args.config, file_path)
self.app.stdout.write(msg)
class OpenstackConfigUpload(OpenstackConfigMixin, base.BaseCommand):
"""Upload new opesntack configuration from file.
"""
def get_parser(self, prog_name):
parser = super(OpenstackConfigUpload, self).get_parser(prog_name)
self.add_env_arg(parser, required=True)
self.add_node_id_arg(parser)
self.add_node_role_arg(parser)
self.add_file_arg(parser)
return parser
def take_action(self, args):
config = self.client.upload(
path=args.file, cluster_id=args.env,
node_id=args.node, node_role=args.role)
msg = "OpenStack configuration with id {0} " \
"uploaded from file '{0}'\n".format(config.id, args.file)
self.app.stdout.write(msg)
class OpenstackConfigExecute(OpenstackConfigMixin, base.BaseCommand):
"""Execute openstack configuration deployment.
"""
def get_parser(self, prog_name):
parser = super(OpenstackConfigExecute, self).get_parser(prog_name)
self.add_env_arg(parser, required=True)
self.add_node_id_arg(parser)
self.add_node_role_arg(parser)
return parser
def take_action(self, args):
self.client.execute(
cluster_id=args.env, node_id=args.node, node_role=args.role)
msg = "OpenStack configuration execution started.\n"
self.app.stdout.write(msg)

View File

@@ -0,0 +1,85 @@
# Copyright 2015 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
class TestOpenstackConfig(test_engine.BaseCLITest):
CLUSTER_ID = 42
NODE_ID = 64
def _test_config_list(self, cmd_line, expected_kwargs):
self.m_get_client.reset_mock()
self.m_client.get_filtered.reset_mock()
self.exec_command('openstack-config list {0}'.format(cmd_line))
self.m_get_client.assert_called_once_with('openstack-config',
mock.ANY)
self.m_client.get_filtered.assert_called_once_with(
**expected_kwargs)
def test_config_list_for_node(self):
self._test_config_list(
cmd_line='--env {0} --node {1}'.format(self.CLUSTER_ID,
self.NODE_ID),
expected_kwargs={'cluster_id': self.CLUSTER_ID,
'node_id': self.NODE_ID, 'node_role': None,
'is_active': True}
)
def test_config_list_for_role(self):
self._test_config_list(
cmd_line='--env {0} --role compute'.format(self.CLUSTER_ID),
expected_kwargs={'cluster_id': self.CLUSTER_ID, 'node_id': None,
'node_role': 'compute', 'is_active': True}
)
def test_config_list_for_cluster(self):
self._test_config_list(
cmd_line='--env {0}'.format(self.CLUSTER_ID),
expected_kwargs={'cluster_id': self.CLUSTER_ID, 'node_id': None,
'node_role': None, 'is_active': True}
)
def test_config_upload(self):
self.m_client.upload.return_value = 'config.yaml'
cmd = 'openstack-config upload --env {0} --node {1} --file ' \
'config.yaml'.format(self.CLUSTER_ID, self.NODE_ID)
self.exec_command(cmd)
self.m_get_client.assert_called_once_with('openstack-config', mock.ANY)
self.m_client.upload.assert_called_once_with(
path='config.yaml', cluster_id=self.CLUSTER_ID,
node_id=self.NODE_ID, node_role=None)
def test_config_download(self):
self.m_client.download.return_value = 'config.yaml'
cmd = 'openstack-config download 1 --file config.yaml'
self.exec_command(cmd)
self.m_get_client.assert_called_once_with('openstack-config', mock.ANY)
self.m_client.download.assert_called_once_with(1, 'config.yaml')
def test_config_execute(self):
cmd = 'openstack-config execute --env {0} --node {1}' \
''.format(self.CLUSTER_ID, self.NODE_ID)
self.exec_command(cmd)
self.m_get_client.assert_called_once_with('openstack-config', mock.ANY)
self.m_client.execute.assert_called_once_with(
cluster_id=self.CLUSTER_ID, node_id=self.NODE_ID, node_role=None)

View File

@@ -16,6 +16,7 @@ from fuelclient.v1 import environment
from fuelclient.v1 import fuelversion
from fuelclient.v1 import network_group
from fuelclient.v1 import node
from fuelclient.v1 import openstack_config
from fuelclient.v1 import task
from fuelclient.v1 import plugins
@@ -24,5 +25,6 @@ __all__ = ('environment',
'fuelversion',
'network_group',
'node',
'openstack_config',
'plugins',
'task',)

View File

@@ -0,0 +1,44 @@
# Copyright 2015 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 OpenstackConfigClient(base_v1.BaseV1Client):
_entity_wrapper = objects.OpenstackConfig
def upload(self, path, cluster_id, node_id=None, node_role=None):
data = self._entity_wrapper.read_file(path)
return self._entity_wrapper.create(
cluster_id=cluster_id, configuration=data['configuration'],
node_id=node_id, node_role=node_role)
def download(self, config_id, path):
config = self._entity_wrapper(config_id)
config.write_file(path, {
'configuration': config.data['configuration']})
return path
def execute(self, **kwargs):
return self._entity_wrapper.execute(**kwargs)
def get_filtered(self, **kwargs):
return self._entity_wrapper.get_filtered_data(**kwargs)
def get_client():
return OpenstackConfigClient()

View File

@@ -59,6 +59,10 @@ fuelclient =
plugins_sync=fuelclient.commands.plugins:PluginsSync
task_list=fuelclient.commands.task:TaskList
task_show=fuelclient.commands.task:TaskShow
openstack-config_list=fuelclient.commands.openstack_config:OpenstackConfigList
openstack-config_upload=fuelclient.commands.openstack_config:OpenstackConfigUpload
openstack-config_download=fuelclient.commands.openstack_config:OpenstackConfigDownload
openstack-config_execute=fuelclient.commands.openstack_config:OpenstackConfigExecute
[global]
setup-hooks =