Add ability to create role for cluster

Add -e/--env to all role commands

DocImpact

Change-Id: Ibab8720b3b48fd61dabccbd30952b3be582b010f
Implements: blueprint role-decomposition
(cherry picked from commit f1ca20421a)
This commit is contained in:
Mikhail 2016-11-09 16:40:42 +03:00
parent 1709a59044
commit a781f17b0c
13 changed files with 1070 additions and 63 deletions

View File

@ -73,6 +73,7 @@ def get_client(resource, version='v1', connection=None):
'openstack-config': v1.openstack_config,
'plugins': v1.plugins,
'release': v1.release,
'role': v1.role,
'sequence': v1.sequence,
'task': v1.task,
'vip': v1.vip

View File

@ -15,6 +15,7 @@
from fuelclient.cli.actions.base import Action
from fuelclient.cli.actions.base import check_all
from fuelclient.cli.actions.base import check_any
import fuelclient.cli.arguments as Args
from fuelclient.cli.arguments import group
from fuelclient.cli.formatting import format_table
@ -23,18 +24,26 @@ from fuelclient.objects.role import Role
class RoleAction(Action):
"""List all roles for specific release
"""List all roles for specific release or cluster
"""
action_name = "role"
fields_mapper = (
('env', 'clusters'),
('release', 'releases')
)
def __init__(self):
# NOTE(dshulyak) this serializers are really messed up
# it gets overwritten in several places
self.file_serializer = FileFormatBasedSerializer()
self.args = [
Args.get_list_arg("List all roles"),
Args.get_release_arg("Release id"),
group(
Args.get_env_arg(),
Args.get_release_arg("Release id"),
required=True
),
Args.get_str_arg("role", help="Name of the role"),
Args.get_file_arg("File with role description"),
@ -52,13 +61,21 @@ class RoleAction(Action):
(None, self.list),
)
@check_all('release')
def parse_model(self, args):
for param, role_class in self.fields_mapper:
model_id = getattr(args, param)
if model_id:
return role_class, model_id
@check_any('release', 'env')
def list(self, params):
"""Print all available roles
"""Print all available roles for release or cluster
fuel role --rel 1
fuel role --env 1
"""
roles = Role.get_all(params.release)
model, model_id = self.parse_model(params)
roles = Role(owner_type=model, owner_id=model_id).get_all()
acceptable_keys = ("name", )
@ -70,46 +87,69 @@ class RoleAction(Action):
)
)
@check_all('role', 'release', 'file')
@check_all('role', 'file')
@check_any('release', 'env')
def item(self, params):
"""Save full role description to file
fuel role --rel 1 --role controller --file some.yaml
fuel role --env 1 --role controller --file some.yaml
"""
role = Role.get_one(params.release, params.role)
model, model_id = self.parse_model(params)
role = Role(owner_type=model, owner_id=model_id).get_role(params.role)
self.file_serializer.write_to_file(params.file, role)
self.file_serializer.print_to_output(
role,
"Role successfully saved to {0}.".format(params.file))
"Role {0} for {1} successfully saved to {2}.".format(
params.role,
model,
params.file))
@check_all('file', 'release')
@check_all('file')
@check_any('release', 'env')
def create(self, params):
"""Create a role from file description
fuel role --rel 1 --create --file some.yaml
fuel role --env 1 --create --file some.yaml
"""
model, model_id = self.parse_model(params)
role = self.file_serializer.read_from_file(params.file)
role = Role.create(params.release, role)
role = Role(owner_type=model, owner_id=model_id).create_role(role)
self.file_serializer.print_to_output(
role,
"Role {0} successfully created from {1}.".format(
role['name'], params.file))
"Role {0} for {1} successfully created from {2}.".format(
role['name'], model, params.file))
@check_all('file', 'release')
@check_all('file')
@check_any('release', 'env')
def update(self, params):
"""Update a role from file description
fuel role --rel 1 --update --file some.yaml
fuel role --rel 1 --create --file some.yaml
fuel role --env 1 --create --file some.yaml
"""
model, model_id = self.parse_model(params)
role = self.file_serializer.read_from_file(params.file)
role = Role.update(params.release, role['name'], role)
role = Role(owner_type=model, owner_id=model_id).update_role(
role['name'],
role)
self.file_serializer.print_to_output(
role,
"Role successfully updated from {0}.".format(params.file))
"Role {0} for {1} successfully updated from {2}.".format(
params.role,
model,
params.file))
@check_all('role', 'release')
@check_all('role')
@check_any('release', 'env')
def delete(self, params):
"""Delete role from fuel
fuel role --delete --role controller --rel 1
fuel role --delete --role controller --env 1
"""
Role.delete(params.release, params.role)
model, model_id = self.parse_model(params)
Role(owner_type=model, owner_id=model_id).delete_role(params.role)
self.file_serializer.print_to_output(
{},
"Role with id {0} successfully deleted.".format(params.role))
"Role {0} for {1} with id {2} successfully deleted.".format(
params.role,
model,
model_id))

263
fuelclient/commands/role.py Normal file
View File

@ -0,0 +1,263 @@
# -*- coding: utf-8 -*-
#
# Copyright 2016 Vitalii Kulanov
#
# 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 abc
import os
from oslo_utils import fileutils
import six
from fuelclient.cli import error
from fuelclient.commands import base
from fuelclient.common import data_utils
class RoleMixIn(object):
entity_name = 'role'
supported_file_formats = ('json', 'yaml')
fields_mapper = (
('env', 'clusters'),
('release', 'releases')
)
def parse_model(self, args):
for param, role_class in self.fields_mapper:
model_id = getattr(args, param)
if model_id:
return role_class, model_id
@staticmethod
def get_file_path(directory, owner_type, owner_id, role_name, file_format):
return os.path.join(os.path.abspath(directory),
'{owner}_{id}'.format(owner=owner_type,
id=owner_id),
'{}.{}'.format(role_name, file_format))
@six.add_metaclass(abc.ABCMeta)
class BaseUploadCommand(RoleMixIn, base.BaseCommand):
"""Base class for uploading metadata of a role."""
@abc.abstractproperty
def action(self):
"""String with the name of the action."""
pass
@abc.abstractproperty
def uploader(self):
"""Callable for uploading data."""
pass
def get_parser(self, prog_name):
parser = super(BaseUploadCommand, self).get_parser(prog_name)
group = parser.add_mutually_exclusive_group(required=True)
group.add_argument('-r',
'--release',
type=int,
help='Id of the release')
group.add_argument('-e',
'--env',
type=int,
help='Id of the environment')
parser.add_argument('-n',
'--name',
required=True,
help='Name of role.')
parser.add_argument('-f',
'--format',
required=True,
choices=self.supported_file_formats,
help='Format of serialized role description.')
parser.add_argument('-d',
'--directory',
required=False,
default=os.path.curdir,
help='Source directory. Defaults to '
'the current directory.')
return parser
def take_action(self, parsed_args):
model, model_id = self.parse_model(parsed_args)
params = {"owner_type": model,
"owner_id": model_id,
"role_name": parsed_args.name}
file_path = self.get_file_path(parsed_args.directory,
model,
model_id,
parsed_args.name,
parsed_args.format)
try:
with open(file_path, 'r') as stream:
data = data_utils.safe_load(parsed_args.format, stream)
self.uploader(data, **params)
except (OSError, IOError):
msg = "Could not read description for role '{}' at {}".format(
parsed_args.name, file_path)
raise error.InvalidFileException(msg)
msg = ("Description of role '{role}' for {owner} with id {id} was "
"{action}d from {file_path}\n".format(role=parsed_args.name,
owner=model,
id=model_id,
action=self.action,
file_path=file_path))
self.app.stdout.write(msg)
class RoleList(RoleMixIn, base.BaseListCommand):
"""Show list of all available roles for release or cluster."""
columns = ("name",
"group",
"conflicts",
"description")
def get_parser(self, prog_name):
parser = super(RoleList, self).get_parser(prog_name)
group = parser.add_mutually_exclusive_group(required=True)
group.add_argument('-r',
'--release',
type=int,
help='Id of the release')
group.add_argument('-e',
'--env',
type=int,
help='Id of the environment'
)
return parser
def take_action(self, parsed_args):
model, model_id = self.parse_model(parsed_args)
data = self.client.get_all(model, model_id)
data = data_utils.get_display_data_multi(self.columns, data)
return self.columns, data
class RoleDownload(RoleMixIn, base.BaseCommand):
"""Download full role description to file."""
def get_parser(self, prog_name):
parser = super(RoleDownload, self).get_parser(prog_name)
group = parser.add_mutually_exclusive_group(required=True)
group.add_argument('-r',
'--release',
type=int,
help='Id of the release')
group.add_argument('-e',
'--env',
type=int,
help='Id of the environment'
)
parser.add_argument('-n',
'--name',
required=True,
help='Name of role.')
parser.add_argument('-f',
'--format',
required=True,
choices=self.supported_file_formats,
help='Format of serialized role description.')
parser.add_argument('-d',
'--directory',
required=False,
default=os.path.curdir,
help='Destination directory. Defaults to '
'the current directory.')
return parser
def take_action(self, parsed_args):
model, model_id = self.parse_model(parsed_args)
file_path = self.get_file_path(parsed_args.directory,
model,
model_id,
parsed_args.name,
parsed_args.format)
data = self.client.get_one(model,
model_id,
parsed_args.name)
try:
fileutils.ensure_tree(os.path.dirname(file_path))
fileutils.delete_if_exists(file_path)
with open(file_path, 'w') as stream:
data_utils.safe_dump(parsed_args.format, stream, data)
except (OSError, IOError):
msg = ("Could not store description data "
"for role {} at {}".format(parsed_args.name, file_path))
raise error.InvalidFileException(msg)
msg = ("Description data of role '{}' within {} id {} "
"was stored in {}\n".format(parsed_args.name,
model,
model_id,
file_path))
self.app.stdout.write(msg)
class RoleUpdate(BaseUploadCommand):
"""Update a role from file description."""
action = "update"
@property
def uploader(self):
return self.client.update
class RoleCreate(BaseUploadCommand):
"""Create a role from file description"""
action = "create"
@property
def uploader(self):
return self.client.create
class RoleDelete(RoleMixIn, base.BaseCommand):
"""Delete a role from release or cluster"""
def get_parser(self, prog_name):
parser = super(RoleDelete, self).get_parser(prog_name)
group = parser.add_mutually_exclusive_group(required=True)
group.add_argument('-r',
'--release',
type=int,
help='Id of the release')
group.add_argument('-e',
'--env',
type=int,
help='Id of the environment'
)
parser.add_argument('-n',
'--name',
required=True,
help='Name of role.')
return parser
def take_action(self, parsed_args):
model, model_id = self.parse_model(parsed_args)
self.client.delete(model,
model_id,
parsed_args.name)
msg = "Role '{}' was deleted from {} with id {}\n".format(
parsed_args.name, model, model_id)
self.app.stdout.write(msg)

View File

@ -23,6 +23,7 @@ from fuelclient.objects.node import Node
from fuelclient.objects.node import NodeCollection
from fuelclient.objects.openstack_config import OpenstackConfig
from fuelclient.objects.release import Release
from fuelclient.objects.role import Role
from fuelclient.objects.task import DeployTask
from fuelclient.objects.task import SnapshotTask
from fuelclient.objects.task import Task

View File

@ -18,33 +18,42 @@ from fuelclient.objects.base import BaseObject
class Role(BaseObject):
class_api_path = "releases/{release_id}/roles/"
instance_api_path = "releases/{release_id}/roles/{role_name}/"
instance_api_path = "{owner_type}/{owner_id}/roles/"
class_api_path = "{owner_type}/{owner_id}/roles/{role_name}/"
@classmethod
def get_all(cls, release_id):
return cls.connection.get_request(
cls.class_api_path.format(release_id=release_id))
def __init__(self, owner_type, owner_id, **kwargs):
super(Role, self).__init__(owner_id, **kwargs)
self.owner_type = owner_type
@classmethod
def get_one(cls, release_id, role_name):
return cls.connection.get_request(
cls.instance_api_path.format(
release_id=release_id, role_name=role_name))
def get_all(self):
return self.connection.get_request(
self.instance_api_path.format(
owner_type=self.owner_type,
owner_id=self.id))
@classmethod
def update(cls, release_id, role_name, data):
return cls.connection.put_request(
cls.instance_api_path.format(
release_id=release_id, role_name=role_name), data)
def get_role(self, role_name):
return self.connection.get_request(
self.class_api_path.format(
owner_type=self.owner_type,
owner_id=self.id,
role_name=role_name))
@classmethod
def create(cls, release_id, data):
return cls.connection.post_request(
cls.class_api_path.format(release_id=release_id), data)
def update_role(self, role_name, data):
return self.connection.put_request(
self.class_api_path.format(
owner_type=self.owner_type,
owner_id=self.id,
role_name=role_name),
data)
@classmethod
def delete(cls, release_id, role_name):
return cls.connection.delete_request(
cls.instance_api_path.format(
release_id=release_id, role_name=role_name))
def create_role(self, data):
return self.connection.post_request(
self.instance_api_path.format(
owner_type=self.owner_type, owner_id=self.id), data)
def delete_role(self, role_name):
return self.connection.delete_request(
self.class_api_path.format(
owner_type=self.owner_type,
owner_id=self.id,
role_name=role_name))

View File

@ -26,11 +26,21 @@ API_OUT = yaml.load(API_IN)
class TestRoleActions(base.UnitTestCase):
release_id = 2
owner_id = 2
def test_list_roles(self):
url = '/api/v1/releases/{0}/roles/'.format(self.release_id)
cmd = 'fuel role --rel {0}'.format(self.release_id)
def test_list_release_roles(self):
url = '/api/v1/releases/{0}/roles/'.format(self.owner_id)
cmd = 'fuel role --rel {0}'.format(self.owner_id)
get_request = self.m_request.get(url, json=[API_OUT])
self.execute(cmd.split())
self.assertTrue(get_request.called)
self.assertIn(url, get_request.last_request.url)
def test_list_cluster_roles(self):
url = '/api/v1/clusters/{0}/roles/'.format(self.owner_id)
cmd = 'fuel role --env {0}'.format(self.owner_id)
get_request = self.m_request.get(url, json=[API_OUT])
self.execute(cmd.split())
@ -39,10 +49,10 @@ class TestRoleActions(base.UnitTestCase):
self.assertIn(url, get_request.last_request.url)
@patch('fuelclient.cli.serializers.open', create=True)
def test_get_role(self, mopen):
url = '/api/v1/releases/{0}/roles/my_role/'.format(self.release_id)
def test_get_release_role(self, mopen):
url = '/api/v1/releases/{0}/roles/my_role/'.format(self.owner_id)
cmd = 'fuel role --role my_role --file myfile.yaml --rel {0}'.format(
self.release_id)
self.owner_id)
get_request = self.m_request.get(url, json=API_OUT)
self.execute(cmd.split())
@ -52,10 +62,23 @@ class TestRoleActions(base.UnitTestCase):
self.assertIn(url, get_request.last_request.url)
@patch('fuelclient.cli.serializers.open', create=True)
def test_create_role(self, mopen):
url = '/api/v1/releases/{0}/roles/'.format(self.release_id)
def test_get_cluster_role(self, mopen):
url = '/api/v1/clusters/{0}/roles/my_role/'.format(self.owner_id)
cmd = 'fuel role --role my_role --file myfile.yaml --env {0}'.format(
self.owner_id)
get_request = self.m_request.get(url, json=API_OUT)
self.execute(cmd.split())
mopen().__enter__().write.assert_called_once_with(API_IN)
self.assertTrue(get_request.called)
self.assertIn(url, get_request.last_request.url)
@patch('fuelclient.cli.serializers.open', create=True)
def test_create_release_role(self, mopen):
url = '/api/v1/releases/{0}/roles/'.format(self.owner_id)
cmd = 'fuel role --create --file myfile.yaml --rel {0}'.format(
self.release_id)
self.owner_id)
mopen().__enter__().read.return_value = API_IN
post_request = self.m_request.post(url, json=API_OUT)
@ -67,10 +90,25 @@ class TestRoleActions(base.UnitTestCase):
API_OUT, post_request.last_request.json())
@patch('fuelclient.cli.serializers.open', create=True)
def test_update_role(self, mopen):
url = '/api/v1/releases/{0}/roles/my_role/'.format(self.release_id)
def test_create_cluster_role(self, mopen):
url = '/api/v1/clusters/{0}/roles/'.format(self.owner_id)
cmd = 'fuel role --create --file myfile.yaml --env {0}'.format(
self.owner_id)
mopen().__enter__().read.return_value = API_IN
post_request = self.m_request.post(url, json=API_OUT)
self.execute(cmd.split())
self.assertTrue(post_request.called)
self.assertIn(url, post_request.last_request.url)
self.assertEqual(
API_OUT, post_request.last_request.json())
@patch('fuelclient.cli.serializers.open', create=True)
def test_update_release_role(self, mopen):
url = '/api/v1/releases/{0}/roles/my_role/'.format(self.owner_id)
cmd = 'fuel role --update --file myfile.yaml --rel {0}'.format(
self.release_id)
self.owner_id)
mopen().__enter__().read.return_value = API_IN
put_request = self.m_request.put(url, json=API_OUT)
@ -81,10 +119,36 @@ class TestRoleActions(base.UnitTestCase):
self.assertEqual(
API_OUT, put_request.last_request.json())
def test_delete_role(self):
url = '/api/v1/releases/{0}/roles/my_role/'.format(self.release_id)
@patch('fuelclient.cli.serializers.open', create=True)
def test_update_cluster_role(self, mopen):
url = '/api/v1/clusters/{0}/roles/my_role/'.format(self.owner_id)
cmd = 'fuel role --update --file myfile.yaml --env {0}'.format(
self.owner_id)
mopen().__enter__().read.return_value = API_IN
put_request = self.m_request.put(url, json=API_OUT)
self.execute(cmd.split())
self.assertTrue(put_request.called)
self.assertIn(url, put_request.last_request.url)
self.assertEqual(
API_OUT, put_request.last_request.json())
def test_delete_release_role(self):
url = '/api/v1/releases/{0}/roles/my_role/'.format(self.owner_id)
cmd = 'fuel role --delete --role my_role --rel {0}'.format(
self.release_id)
self.owner_id)
delete_request = self.m_request.delete(url, json=API_OUT)
self.execute(cmd.split())
self.assertTrue(delete_request.called)
self.assertIn(url, delete_request.last_request.url)
def test_delete_cluster_role(self):
url = '/api/v1/clusters/{0}/roles/my_role/'.format(self.owner_id)
cmd = 'fuel role --delete --role my_role --env {0}'.format(
self.owner_id)
delete_request = self.m_request.delete(url, json=API_OUT)
self.execute(cmd.split())
@ -93,8 +157,8 @@ class TestRoleActions(base.UnitTestCase):
self.assertIn(url, delete_request.last_request.url)
def test_formatting_for_list_roles(self):
url = '/api/v1/releases/{0}/roles/'.format(self.release_id)
cmd = 'fuel role --rel {0}'.format(self.release_id)
url = '/api/v1/releases/{0}/roles/'.format(self.owner_id)
cmd = 'fuel role --rel {0}'.format(self.owner_id)
get_request = self.m_request.get(url, json=[API_OUT])
with patch.object(Serializer, 'print_to_output') as mock_print:

View File

@ -0,0 +1,342 @@
# -*- coding: utf-8 -*-
#
# Copyright 2016 Vitalii Kulanov
#
# 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 json
import mock
import yaml
from fuelclient.tests.unit.v2.cli import test_engine
from fuelclient.tests.utils import fake_role
class TestRoleCommand(test_engine.BaseCLITest):
"""Tests for fuel2 role * commands."""
def test_role_list_for_release(self):
self.m_client.get_all.return_value = [
{"name": "fake_role_1",
"group": "fake_group",
"conflicts": ["fake_role_2", "fake_role_3"],
"description": "some fake description"},
{"name": "fake_role_2",
"group": "fake_group",
"conflicts": ["fake_role_1", "fake_role_3"],
"description": "some fake description"}
]
release_id = 45
args = 'role list -r {id}'.format(id=release_id)
self.exec_command(args)
self.m_client.get_all.assert_called_once_with('releases', release_id)
self.m_get_client.assert_called_once_with('role', mock.ANY)
def test_role_list_for_cluster(self):
self.m_client.get_all.return_value = [
{"name": "fake_role_1",
"group": "fake_group",
"conflicts": ["fake_role_2", "fake_role_3"],
"description": "some fake description"},
{"name": "fake_role_2",
"group": "fake_group",
"conflicts": ["fake_role_1", "fake_role_3"],
"description": "some fake description"}
]
env_id = 45
args = 'role list -e {id}'.format(id=env_id)
self.exec_command(args)
self.m_client.get_all.assert_called_once_with('clusters', env_id)
self.m_get_client.assert_called_once_with('role', mock.ANY)
@mock.patch('sys.stderr')
def test_role_list_fail(self, mocked_stderr):
args = 'role list'
self.assertRaises(SystemExit, self.exec_command, args)
self.assertIn('-r/--release -e/--env',
mocked_stderr.write.call_args_list[-1][0][0])
@mock.patch('sys.stderr')
def test_role_list_with_mutually_exclusive_params(self, mocked_stderr):
args = 'role list -e 1 -r 2'
self.assertRaises(SystemExit, self.exec_command, args)
self.assertIn('not allowed',
mocked_stderr.write.call_args_list[-1][0][0])
@mock.patch('json.dump')
def test_release_role_download_json(self, m_dump):
release_id = 45
role_name = 'fake_role'
test_data = fake_role.get_fake_role(fake_role)
args = 'role download -r {} -n {} -f json -d /tmp'.format(release_id,
role_name)
expected_path = '/tmp/releases_{id}/{name}.json'.format(id=release_id,
name=role_name)
self.m_client.get_one.return_value = test_data
m_open = mock.mock_open()
with mock.patch('fuelclient.commands.role.open', m_open, create=True):
self.exec_command(args)
m_open.assert_called_once_with(expected_path, 'w')
m_dump.assert_called_once_with(test_data, mock.ANY, indent=4)
self.m_get_client.assert_called_once_with('role', mock.ANY)
self.m_client.get_one.assert_called_once_with('releases',
release_id,
role_name)
@mock.patch('json.dump')
def test_cluster_role_download_json(self, m_dump):
env_id = 45
role_name = 'fake_role'
test_data = fake_role.get_fake_role(fake_role)
args = 'role download -e {} -n {} -f json -d /tmp'.format(env_id,
role_name)
expected_path = '/tmp/clusters_{id}/{name}.json'.format(id=env_id,
name=role_name)
self.m_client.get_one.return_value = test_data
m_open = mock.mock_open()
with mock.patch('fuelclient.commands.role.open', m_open, create=True):
self.exec_command(args)
m_open.assert_called_once_with(expected_path, 'w')
m_dump.assert_called_once_with(test_data, mock.ANY, indent=4)
self.m_get_client.assert_called_once_with('role', mock.ANY)
self.m_client.get_one.assert_called_once_with('clusters',
env_id,
role_name)
@mock.patch('yaml.safe_dump')
def test_release_role_download_yaml(self, m_safe_dump):
release_id = 45
role_name = 'fake_role'
test_data = fake_role.get_fake_role(fake_role)
args = 'role download -r {} -n {} -f yaml -d /tmp'.format(release_id,
role_name)
expected_path = '/tmp/releases_{id}/{name}.yaml'.format(id=release_id,
name=role_name)
self.m_client.get_one.return_value = test_data
m_open = mock.mock_open()
with mock.patch('fuelclient.commands.role.open', m_open, create=True):
self.exec_command(args)
m_open.assert_called_once_with(expected_path, 'w')
m_safe_dump.assert_called_once_with(test_data, mock.ANY,
default_flow_style=False)
self.m_get_client.assert_called_once_with('role', mock.ANY)
self.m_client.get_one.assert_called_once_with('releases',
release_id,
role_name)
@mock.patch('yaml.safe_dump')
def test_cluster_role_download_yaml(self, m_safe_dump):
env_id = 45
role_name = 'fake_role'
test_data = fake_role.get_fake_role(fake_role)
args = 'role download -e {} -n {} -f yaml -d /tmp'.format(env_id,
role_name)
expected_path = '/tmp/clusters_{id}/{name}.yaml'.format(id=env_id,
name=role_name)
self.m_client.get_one.return_value = test_data
m_open = mock.mock_open()
with mock.patch('fuelclient.commands.role.open', m_open, create=True):
self.exec_command(args)
m_open.assert_called_once_with(expected_path, 'w')
m_safe_dump.assert_called_once_with(test_data, mock.ANY,
default_flow_style=False)
self.m_get_client.assert_called_once_with('role', mock.ANY)
self.m_client.get_one.assert_called_once_with('clusters',
env_id,
role_name)
def test_release_role_update_json(self):
release_id = 45
role_name = 'fake_role'
params = {"owner_type": "releases",
"owner_id": release_id,
"role_name": role_name}
args = 'role update -r {} -n {} -f json -d /tmp'.format(release_id,
role_name)
test_data = fake_role.get_fake_role(role_name)
expected_path = '/tmp/releases_{}/fake_role.json'.format(release_id)
m_open = mock.mock_open(read_data=json.dumps(test_data))
with mock.patch('fuelclient.commands.role.open', m_open, create=True):
self.exec_command(args)
m_open.assert_called_once_with(expected_path, 'r')
self.m_get_client.assert_called_once_with('role', mock.ANY)
self.m_client.update.assert_called_once_with(test_data, **params)
def test_cluster_role_update_json(self):
env_id = 45
role_name = 'fake_role'
params = {"owner_type": "clusters",
"owner_id": env_id,
"role_name": role_name}
args = 'role update -e {} -n {} -f json -d /tmp'.format(env_id,
role_name)
test_data = fake_role.get_fake_role(role_name)
expected_path = '/tmp/clusters_{}/fake_role.json'.format(env_id)
m_open = mock.mock_open(read_data=json.dumps(test_data))
with mock.patch('fuelclient.commands.role.open', m_open, create=True):
self.exec_command(args)
m_open.assert_called_once_with(expected_path, 'r')
self.m_get_client.assert_called_once_with('role', mock.ANY)
self.m_client.update.assert_called_once_with(test_data, **params)
def test_release_role_update_yaml(self):
release_id = 45
role_name = 'fake_role'
params = {"owner_type": "releases",
"owner_id": release_id,
"role_name": role_name}
args = 'role update -r {} -n {} -f yaml -d /tmp'.format(release_id,
role_name)
test_data = fake_role.get_fake_role(role_name)
expected_path = '/tmp/releases_{}/fake_role.yaml'.format(release_id)
m_open = mock.mock_open(read_data=yaml.safe_dump(test_data))
with mock.patch('fuelclient.commands.role.open', m_open, create=True):
self.exec_command(args)
m_open.assert_called_once_with(expected_path, 'r')
self.m_get_client.assert_called_once_with('role', mock.ANY)
self.m_client.update.assert_called_once_with(test_data, **params)
def test_cluster_role_update_yaml(self):
env_id = 45
role_name = 'fake_role'
params = {"owner_type": "clusters",
"owner_id": env_id,
"role_name": role_name}
args = 'role update -e {} -n {} -f yaml -d /tmp'.format(env_id,
role_name)
test_data = fake_role.get_fake_role(role_name)
expected_path = '/tmp/clusters_{}/fake_role.yaml'.format(env_id)
m_open = mock.mock_open(read_data=yaml.safe_dump(test_data))
with mock.patch('fuelclient.commands.role.open', m_open, create=True):
self.exec_command(args)
m_open.assert_called_once_with(expected_path, 'r')
self.m_get_client.assert_called_once_with('role', mock.ANY)
self.m_client.update.assert_called_once_with(test_data, **params)
def test_release_role_create_json(self):
release_id = 45
role_name = 'fake_role'
params = {"owner_type": "releases",
"owner_id": release_id,
"role_name": role_name}
args = 'role create -r {} -n {} -f json -d /tmp'.format(release_id,
role_name)
test_data = fake_role.get_fake_role(role_name)
expected_path = '/tmp/releases_{}/fake_role.json'.format(release_id)
m_open = mock.mock_open(read_data=json.dumps(test_data))
with mock.patch('fuelclient.commands.role.open', m_open, create=True):
self.exec_command(args)
m_open.assert_called_once_with(expected_path, 'r')
self.m_get_client.assert_called_once_with('role', mock.ANY)
self.m_client.create.assert_called_once_with(test_data, **params)
def test_cluster_role_create_json(self):
env_id = 45
role_name = 'fake_role'
params = {"owner_type": "clusters",
"owner_id": env_id,
"role_name": role_name}
args = 'role create -e {} -n {} -f json -d /tmp'.format(env_id,
role_name)
test_data = fake_role.get_fake_role(role_name)
expected_path = '/tmp/clusters_{}/fake_role.json'.format(env_id)
m_open = mock.mock_open(read_data=json.dumps(test_data))
with mock.patch('fuelclient.commands.role.open', m_open, create=True):
self.exec_command(args)
m_open.assert_called_once_with(expected_path, 'r')
self.m_get_client.assert_called_once_with('role', mock.ANY)
self.m_client.create.assert_called_once_with(test_data, **params)
def test_release_role_create_yaml(self):
release_id = 45
role_name = 'fake_role'
params = {"owner_type": "releases",
"owner_id": release_id,
"role_name": role_name}
args = 'role create -r {} -n {} -f yaml -d /tmp'.format(release_id,
role_name)
test_data = fake_role.get_fake_role(role_name)
expected_path = '/tmp/releases_{}/fake_role.yaml'.format(release_id)
m_open = mock.mock_open(read_data=yaml.safe_dump(test_data))
with mock.patch('fuelclient.commands.role.open', m_open, create=True):
self.exec_command(args)
m_open.assert_called_once_with(expected_path, 'r')
self.m_get_client.assert_called_once_with('role', mock.ANY)
self.m_client.create.assert_called_once_with(test_data, **params)
def test_cluster_role_create_yaml(self):
env_id = 45
role_name = 'fake_role'
params = {"owner_type": "clusters",
"owner_id": env_id,
"role_name": role_name}
args = 'role create -e {} -n {} -f yaml -d /tmp'.format(env_id,
role_name)
test_data = fake_role.get_fake_role(role_name)
expected_path = '/tmp/clusters_{}/fake_role.yaml'.format(env_id)
m_open = mock.mock_open(read_data=yaml.safe_dump(test_data))
with mock.patch('fuelclient.commands.role.open', m_open, create=True):
self.exec_command(args)
m_open.assert_called_once_with(expected_path, 'r')
self.m_get_client.assert_called_once_with('role', mock.ANY)
self.m_client.create.assert_called_once_with(test_data, **params)
def test_release_role_delete(self):
release_id = 45
role_name = 'fake_role'
args = 'role delete -r {} -n {}'.format(release_id, role_name)
self.exec_command(args)
self.m_get_client.assert_called_once_with('role', mock.ANY)
self.m_client.delete.assert_called_once_with('releases',
release_id,
role_name)
def test_cluster_role_delete(self):
env_id = 45
role_name = 'fake_role'
args = 'role delete -e {} -n {}'.format(env_id, role_name)
self.exec_command(args)
self.m_get_client.assert_called_once_with('role', mock.ANY)
self.m_client.delete.assert_called_once_with('clusters',
env_id,
role_name)

View File

@ -0,0 +1,160 @@
# -*- coding: utf-8 -*-
#
# Copyright 2016 Vitalii Kulanov
#
# 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 TestRoleFacade(test_api.BaseLibTest):
def setUp(self):
super(TestRoleFacade, self).setUp()
self.version = 'v1'
self.role_name = 'fake_role'
self.fake_role = utils.get_fake_role(self.role_name)
self.fake_roles = utils.get_fake_roles(10)
self.client = fuelclient.get_client('role', self.version)
def get_uri(self, owner):
return '/api/{version}/{owner}/'.format(version=self.version,
owner=owner)
def test_release_role_list(self):
owner, owner_id = 'releases', 42
expected_uri = self.get_object_uri(self.get_uri(owner),
owner_id,
'/roles/')
matcher = self.m_request.get(expected_uri, json=self.fake_roles)
self.client.get_all(owner, owner_id)
self.assertTrue(matcher.called)
def test_cluster_role_list(self):
owner, owner_id = 'clusters', 42
expected_uri = self.get_object_uri(self.get_uri(owner),
owner_id,
'/roles/')
matcher = self.m_request.get(expected_uri, json=self.fake_roles)
self.client.get_all(owner_type=owner, owner_id=owner_id)
self.assertTrue(matcher.called)
def test_release_role_download(self):
owner, owner_id = 'releases', 45
expected_uri = self.get_object_uri(self.get_uri(owner),
owner_id,
'/roles/{}/'.format(self.role_name))
role_matcher = self.m_request.get(expected_uri, json=self.fake_role)
role = self.client.get_one(owner, owner_id, self.role_name)
self.assertTrue(expected_uri, role_matcher.called)
self.assertEqual(role, self.fake_role)
def test_cluster_role_download(self):
owner, owner_id = 'clusters', 45
expected_uri = self.get_object_uri(self.get_uri(owner),
owner_id,
'/roles/{}/'.format(self.role_name))
role_matcher = self.m_request.get(expected_uri, json=self.fake_role)
role = self.client.get_one(owner, owner_id, self.role_name)
self.assertTrue(expected_uri, role_matcher.called)
self.assertEqual(role, self.fake_role)
def test_release_role_update(self):
owner, owner_id = 'releases', 45
params = {"owner_type": owner,
"owner_id": owner_id,
"role_name": self.role_name}
expected_uri = self.get_object_uri(self.get_uri(owner),
owner_id,
'/roles/{}/'.format(self.role_name))
upd_matcher = self.m_request.put(expected_uri, json=self.fake_role)
self.client.update(self.fake_role, **params)
self.assertTrue(upd_matcher.called)
self.assertEqual(self.fake_role, upd_matcher.last_request.json())
def test_cluster_role_update(self):
owner, owner_id = 'clusters', 45
params = {"owner_type": owner,
"owner_id": owner_id,
"role_name": self.role_name}
expected_uri = self.get_object_uri(self.get_uri(owner),
owner_id,
'/roles/{}/'.format(self.role_name))
upd_matcher = self.m_request.put(expected_uri, json=self.fake_role)
self.client.update(self.fake_role, **params)
self.assertTrue(upd_matcher.called)
self.assertEqual(self.fake_role, upd_matcher.last_request.json())
def test_release_role_create(self):
owner, owner_id = 'releases', 45
params = {"owner_type": owner,
"owner_id": owner_id}
expected_uri = self.get_object_uri(self.get_uri(owner),
owner_id,
'/roles/')
post_matcher = self.m_request.post(expected_uri, json=self.fake_role)
self.client.create(self.fake_role, **params)
self.assertTrue(post_matcher.called)
self.assertEqual(self.fake_role, post_matcher.last_request.json())
def test_cluster_role_create(self):
owner, owner_id = 'clusters', 45
params = {"owner_type": owner,
"owner_id": owner_id}
expected_uri = self.get_object_uri(self.get_uri(owner),
owner_id,
'/roles/')
post_matcher = self.m_request.post(expected_uri, json=self.fake_role)
self.client.create(self.fake_role, **params)
self.assertTrue(post_matcher.called)
self.assertEqual(self.fake_role, post_matcher.last_request.json())
def test_release_role_delete(self):
owner, owner_id = 'releases', 45
expected_uri = self.get_object_uri(self.get_uri(owner),
owner_id,
'/roles/{}/'.format(self.role_name))
delete_matcher = self.m_request.delete(expected_uri, json={})
self.client.delete(owner, owner_id, self.role_name)
self.assertTrue(delete_matcher.called)
def test_cluster_role_delete(self):
owner, owner_id = 'clusters', 45
expected_uri = self.get_object_uri(self.get_uri(owner),
owner_id,
'/roles/{}/'.format(self.role_name))
delete_matcher = self.m_request.delete(expected_uri, json={})
self.client.delete(owner, owner_id, self.role_name)
self.assertTrue(delete_matcher.called)

View File

@ -48,6 +48,8 @@ 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
from fuelclient.tests.utils.fake_role import get_fake_role
from fuelclient.tests.utils.fake_role import get_fake_roles
__all__ = (get_fake_deployment_history,
@ -62,6 +64,8 @@ __all__ = (get_fake_deployment_history,
get_fake_attributes_metadata,
get_fake_release_component,
get_fake_release_components,
get_fake_role,
get_fake_roles,
get_fake_fuel_version,
get_fake_interface_config,
get_fake_network_group,

View File

@ -0,0 +1,49 @@
# -*- 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_role(name=None, meta=None, volumes_roles_mapping=None):
"""Create a random fake role
Returns the serialized and parametrized representation of a dumped Fuel
role. Represents the average amount of data.
"""
return {
"name": name or "controller",
"meta": meta or {
"group": "base",
"name": "Controller",
"conflicts": ["compute", "ceph-osd"],
"description": "The Controller initiates orchestration activities "
"and provides an external API. Other components "
"like Glance (image storage), Keystone (identity "
"management), Horizon (OpenStack dashboard) and "
"Nova-Scheduler are installed on the controller "
"as well."
},
"volumes_roles_mapping": volumes_roles_mapping or [
{"id": "os", "allocate_size": "min"},
{"id": "logs", "allocate_size": "min"},
{"id": "image", "allocate_size": "all"},
{"id": "mysql", "allocate_size": "min"},
{"id": "horizon", "allocate_size": "min"}
]
}
def get_fake_roles(role_count, **kwargs):
"""Create a random fake list of roles."""
return [get_fake_role(**kwargs)
for _ in range(role_count)]

View File

@ -24,6 +24,7 @@ from fuelclient.v1 import network_group
from fuelclient.v1 import node
from fuelclient.v1 import openstack_config
from fuelclient.v1 import release
from fuelclient.v1 import role
from fuelclient.v1 import plugins
from fuelclient.v1 import sequence
from fuelclient.v1 import task
@ -43,6 +44,7 @@ __all__ = ('cluster_settings',
'openstack_config',
'plugins',
'release',
'role',
'sequence',
'task',
'vip')

67
fuelclient/v1/role.py Normal file
View File

@ -0,0 +1,67 @@
# -*- coding: utf-8 -*-
#
# Copyright 2016 Vitalii Kulanov
#
# 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 RoleClient(base_v1.BaseV1Client):
_entity_wrapper = objects.Role
def get_all(self, owner_type, owner_id):
"""Get all available roles for specific release or cluster.
:param owner_type: release or cluster
:type owner_id: int
:return: roles data as a list of dict
:rtype: list
"""
data = self._entity_wrapper(owner_type, owner_id).get_all()
# Retrieve nested data from 'meta' and add it as a new key-value pair
for role in data:
role_meta = role.pop('meta')
role['group'] = role_meta.get('group')
role['conflicts'] = role_meta.get('conflicts')
role['description'] = role_meta.get('description')
return data
def get_one(self, owner_type, owner_id, role_name):
role = self._entity_wrapper(owner_type, owner_id)
return role.get_role(role_name)
def update(self, data, **kwargs):
role = self._entity_wrapper(owner_type=kwargs['owner_type'],
owner_id=kwargs['owner_id'])
return role.update_role(kwargs['role_name'], data)
def create(self, data, **kwargs):
role = self._entity_wrapper(owner_type=kwargs['owner_type'],
owner_id=kwargs['owner_id'])
return role.create_role(data)
def delete(self, owner_type, owner_id, role_name):
role = self._entity_wrapper(owner_type=owner_type,
owner_id=owner_id)
return role.delete_role(role_name)
def get_client(connection):
return RoleClient(connection)

View File

@ -101,6 +101,11 @@ fuelclient =
release_list=fuelclient.commands.release:ReleaseList
release_repos_list=fuelclient.commands.release:ReleaseReposList
release_repos_update=fuelclient.commands.release:ReleaseReposUpdate
role_create=fuelclient.commands.role:RoleCreate
role_delete=fuelclient.commands.role:RoleDelete
role_download=fuelclient.commands.role:RoleDownload
role_list=fuelclient.commands.role:RoleList
role_update=fuelclient.commands.role:RoleUpdate
task_delete=fuelclient.commands.task:TaskDelete
task_deployment-info_download=fuelclient.commands.task:TaskDeploymentInfoDownload
task_history_show=fuelclient.commands.task:TaskHistoryShow