diff --git a/fuelclient/cli/actions/role.py b/fuelclient/cli/actions/role.py index 65f6f61..234b4d3 100644 --- a/fuelclient/cli/actions/role.py +++ b/fuelclient/cli/actions/role.py @@ -14,9 +14,12 @@ from fuelclient.cli.actions.base import Action +from fuelclient.cli.actions.base import check_all import fuelclient.cli.arguments as Args +from fuelclient.cli.arguments import group from fuelclient.cli.formatting import format_table -from fuelclient.objects.release import Release +from fuelclient.cli.serializers import FileFormatBasedSerializer +from fuelclient.objects.role import Role class RoleAction(Action): @@ -25,30 +28,40 @@ class RoleAction(Action): action_name = "role" def __init__(self): - super(RoleAction, self).__init__() + # 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 for specific release"), - Args.get_release_arg("Release id", required=True) + Args.get_list_arg("List all roles"), + + Args.get_release_arg("Release id"), + Args.get_str_arg("role", help="Name of the role"), + Args.get_file_arg("File with role description"), + + group( + Args.get_create_arg("Create role from file"), + Args.get_boolean_arg("update", help="Update role from file"), + Args.get_delete_arg("Delete role from fuel") + ) ] self.flag_func_map = ( + ("delete", self.delete), + ("create", self.create), + ("update", self.update), + ("role", self.item), (None, self.list), ) + @check_all('release') def list(self, params): - """Print all available roles and their - conflicts for some release with id=1: + """Print all available roles + fuel role --rel 1 """ - release = Release(params.release, params=params) - data = release.get_fresh_data() - acceptable_keys = ("name", "conflicts") - roles = [ - { - "name": role_name, - "conflicts": ", ".join( - metadata.get("conflicts", ["-"]) - ) - } for role_name, metadata in data["roles_metadata"].iteritems()] + roles = Role.get_all(params.release) + + acceptable_keys = ("name", "id") + self.serializer.print_to_output( roles, format_table( @@ -56,3 +69,47 @@ class RoleAction(Action): acceptable_keys=acceptable_keys ) ) + + @check_all('role', 'release', 'file') + def item(self, params): + """Save full role description to file + fuel role --rel 1 --role controller --file some.yaml + """ + role = Role.get_one(params.release, 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)) + + @check_all('file', 'release') + def create(self, params): + """Create a role from file description + fuel role --rel 1 --create --file some.yaml + """ + role = self.file_serializer.read_from_file(params.file) + role = Role.create(params.release, role) + self.file_serializer.print_to_output( + role, + "Role {0} successfully created from {1}.".format( + role['name'], params.file)) + + @check_all('file', 'release') + def update(self, params): + """Update a role from file description + fuel role --rel 1 --update --file some.yaml + """ + role = self.file_serializer.read_from_file(params.file) + role = Role.update(params.release, role['name'], role) + self.file_serializer.print_to_output( + role, + "Role successfully updated from {0}.".format(params.file)) + + @check_all('role', 'release') + def delete(self, params): + """Delete role from fuel + fuel role --delete --role controller --rel 1 + """ + Role.delete(params.release, params.role) + self.file_serializer.print_to_output( + {}, + "Role with id {0} successfully deleted.".format(params.role)) diff --git a/fuelclient/cli/arguments.py b/fuelclient/cli/arguments.py index b81504c..85909b3 100644 --- a/fuelclient/cli/arguments.py +++ b/fuelclient/cli/arguments.py @@ -372,6 +372,10 @@ def get_dir_arg(help_msg): return get_str_arg("dir", default=os.curdir, help=help_msg) +def get_file_arg(help_msg): + return get_str_arg("file", help=help_msg) + + def get_verify_arg(help_msg): return get_boolean_arg("verify", flags=("-v",), help=help_msg) diff --git a/fuelclient/cli/serializers.py b/fuelclient/cli/serializers.py index 32ff0f8..ce06b75 100644 --- a/fuelclient/cli/serializers.py +++ b/fuelclient/cli/serializers.py @@ -20,6 +20,8 @@ import os import yaml +from fuelclient.cli import error + class Serializer(object): """Serializer class - contains all logic responsible for @@ -86,6 +88,27 @@ class Serializer(object): return self.serializer["r"](file_to_read.read()) +class FileFormatBasedSerializer(Serializer): + + def get_serializer(self, path): + extension = os.path.splitext(path)[1][1:] + if extension not in self.serializers: + raise error.BadDataException( + 'No serializer for provided file {0}'.format(path)) + return self.serializers[extension] + + def write_to_file(self, full_path, data): + serializer = self.get_serializer(full_path) + with open(full_path, "w+") as f: + f.write(serializer["w"](data)) + return full_path + + def read_from_file(self, full_path): + serializer = self.get_serializer(full_path) + with open(full_path, "r") as f: + return serializer["r"](f.read()) + + def listdir_without_extensions(dir_path): return ifilter( lambda f: f != "", diff --git a/fuelclient/client.py b/fuelclient/client.py index b184b1b..b857ccf 100644 --- a/fuelclient/client.py +++ b/fuelclient/client.py @@ -145,6 +145,7 @@ class Client(object): headers = {'x-auth-token': self.auth_token} params = params or {} + resp = requests.get(url, params=params, headers=headers) resp.raise_for_status() diff --git a/fuelclient/objects/role.py b/fuelclient/objects/role.py new file mode 100644 index 0000000..a866dcc --- /dev/null +++ b/fuelclient/objects/role.py @@ -0,0 +1,50 @@ +# 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.objects.base import BaseObject + + +class Role(BaseObject): + + class_api_path = "releases/{release_id}/roles/" + instance_api_path = "releases/{release_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)) + + @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)) + + @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) + + @classmethod + def create(cls, release_id, data): + return cls.connection.post_request( + cls.class_api_path.format(release_id=release_id), 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)) diff --git a/fuelclient/tests/test_roles.py b/fuelclient/tests/test_roles.py new file mode 100644 index 0000000..b2178fb --- /dev/null +++ b/fuelclient/tests/test_roles.py @@ -0,0 +1,84 @@ +# 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 json + +from mock import patch +import yaml + +from fuelclient.tests import base + +API_IN = """id: 2 +name: my_role +""" + +API_OUT = yaml.load(API_IN) + + +@patch('fuelclient.client.requests') +class TestRoleActions(base.UnitTestCase): + + def test_list_roles(self, mreq): + self.execute(['fuel', 'role', '--rel', '2']) + role_call = mreq.get.call_args_list[-1] + url = role_call[0][0] + self.assertIn('/releases/2/roles/', url) + + @patch('fuelclient.cli.serializers.open', create=True) + def test_get_role(self, mopen, mreq): + mreq.get().json.return_value = API_OUT + self.execute(['fuel', 'role', + '--role', 'my_role', '--file', 'myfile.yaml', + '--rel', '2']) + + mopen().__enter__().write.assert_called_once_with(API_IN) + + call_args = mreq.get.call_args_list[1] + url = call_args[0][0] + self.assertIn('releases/2/roles/my_role', url) + + @patch('fuelclient.cli.serializers.open', create=True) + def test_create_role(self, mopen, mreq): + mopen().__enter__().read.return_value = API_IN + + self.execute(['fuel', 'role', '--create', + '--file', 'myfile.yaml', '--rel', '2']) + + call_args = mreq.post.call_args_list[0] + url = call_args[0][0] + kwargs = call_args[1] + self.assertIn('releases/2/roles/', url) + self.assertEqual( + json.loads(kwargs['data']), API_OUT) + + @patch('fuelclient.cli.serializers.open', create=True) + def test_update_role(self, mopen, mreq): + mopen().__enter__().read.return_value = API_IN + + self.execute(['fuel', 'role', '--update', + '--file', 'myfile.yaml', '--rel', '2']) + + call_args = mreq.put.call_args_list[0] + url = call_args[0][0] + kwargs = call_args[1] + self.assertIn('releases/2/roles/my_role', url) + self.assertEqual( + json.loads(kwargs['data']), API_OUT) + + def test_delete_role(self, mreq): + self.execute(['fuel', 'role', + '--delete', '--role', '3', '--rel', '2']) + role_call = mreq.delete.call_args_list[-1] + url = role_call[0][0] + self.assertIn('releases/2/roles/3', url)