diff --git a/fuelclient/cli/actions/environment.py b/fuelclient/cli/actions/environment.py index a87867d..95ffbb7 100644 --- a/fuelclient/cli/actions/environment.py +++ b/fuelclient/cli/actions/environment.py @@ -207,7 +207,7 @@ class EnvironmentAction(Action): full_path = '{0}/deployment_tasks'.format(dir_path) if params.download: tasks = cluster.get_deployment_tasks() - self.serializer.write_to_file(full_path, tasks) + self.serializer.write_to_path(full_path, tasks) print("Deployment tasks for cluster {0} " "downloaded into {1}.yaml.".format(cluster.id, full_path)) elif params.upload: diff --git a/fuelclient/cli/actions/release.py b/fuelclient/cli/actions/release.py index af94bda..d8bada2 100644 --- a/fuelclient/cli/actions/release.py +++ b/fuelclient/cli/actions/release.py @@ -95,7 +95,7 @@ class ReleaseAction(Action): full_path = '{0}/networks'.format(dir_path) if params.download: networks = release.get_networks() - self.serializer.write_to_file(full_path, networks) + self.serializer.write_to_path(full_path, networks) print("Networks for release {0} " "downloaded into {1}.yaml".format(release.id, full_path)) elif params.upload: @@ -117,7 +117,7 @@ class ReleaseAction(Action): full_path = '{0}/deployment_tasks'.format(dir_path) if params.download: tasks = release.get_deployment_tasks() - self.serializer.write_to_file(full_path, tasks) + self.serializer.write_to_path(full_path, tasks) print("Deployment tasks for release {0} " "downloaded into {1}.yaml.".format(release.id, full_path)) elif params.upload: diff --git a/fuelclient/cli/actions/snapshot.py b/fuelclient/cli/actions/snapshot.py index 8177dbb..f2e77e1 100644 --- a/fuelclient/cli/actions/snapshot.py +++ b/fuelclient/cli/actions/snapshot.py @@ -12,6 +12,10 @@ # License for the specific language governing permissions and limitations # under the License. +import sys + +import yaml + from fuelclient.cli.actions.base import Action import fuelclient.cli.arguments as Args from fuelclient.cli.formatting import download_snapshot_with_progress_bar @@ -27,8 +31,11 @@ class SnapshotAction(Action): super(SnapshotAction, self).__init__() self.args = ( Args.get_dir_arg("Directory to which download snapshot."), + Args.get_boolean_arg("conf", + help_="Provide this flag to generate conf") ) self.flag_func_map = ( + ('conf', self.get_snapshot_config), (None, self.get_snapshot), ) @@ -38,8 +45,17 @@ class SnapshotAction(Action): To download diagnostic snapshot to specific directory: fuel snapshot --dir path/to/directory + + To specify config for snapshoting + fuel snapshot < conf.yaml + """ - snapshot_task = SnapshotTask.start_snapshot_task() + if sys.stdin.isatty(): + conf = {} + else: + conf = yaml.load(sys.stdin.read()) + + snapshot_task = SnapshotTask.start_snapshot_task(conf) self.serializer.print_to_output( snapshot_task.data, "Generating dump..." @@ -49,3 +65,14 @@ class SnapshotAction(Action): snapshot_task.connection.root + snapshot_task.data["message"], directory=params.dir ) + + def get_snapshot_config(self, params): + """Download default config for snapshot + + fuel snapshot --conf > dump_conf.yaml + + To use json formatter + fuel snapshot --conf --json + """ + conf = SnapshotTask.get_default_config() + self.serializer.write_to_file(sys.stdout, conf) diff --git a/fuelclient/cli/serializers.py b/fuelclient/cli/serializers.py index ce06b75..a132511 100644 --- a/fuelclient/cli/serializers.py +++ b/fuelclient/cli/serializers.py @@ -74,10 +74,10 @@ class Serializer(object): path, self.format ) - def write_to_file(self, path, data): + def write_to_path(self, path, data): full_path = self.prepare_path(path) with open(full_path, "w+") as file_to_write: - file_to_write.write(self.serializer["w"](data)) + self.write_to_file(file_to_write, data) return full_path def read_from_file(self, path): @@ -87,6 +87,14 @@ class Serializer(object): with open(full_path, "r") as file_to_read: return self.serializer["r"](file_to_read.read()) + def write_to_file(self, file_obj, data): + """Writes to opened file or file like object + :param file_obj: opened file + :param data: any serializable object + """ + serialized = self.serializer["w"](data) + file_obj.write(serialized) + class FileFormatBasedSerializer(Serializer): diff --git a/fuelclient/objects/environment.py b/fuelclient/objects/environment.py index dd6c4fe..533de6f 100644 --- a/fuelclient/objects/environment.py +++ b/fuelclient/objects/environment.py @@ -134,14 +134,14 @@ class Environment(BaseObject): def write_network_data(self, network_data, directory=os.curdir, serializer=None): - return (serializer or self.serializer).write_to_file( + return (serializer or self.serializer).write_to_path( self.get_network_data_path(directory), network_data ) def write_settings_data(self, settings_data, directory=os.curdir, serializer=None): - return (serializer or self.serializer).write_to_file( + return (serializer or self.serializer).write_to_path( self.get_settings_data_path(directory), settings_data ) @@ -262,7 +262,7 @@ class Environment(BaseObject): os.makedirs(dir_name) if isinstance(facts, dict): engine_file_path = os.path.join(dir_name, "engine") - (serializer or self.serializer).write_to_file( + (serializer or self.serializer).write_to_path( engine_file_path, facts["engine"]) facts = facts["nodes"] name_template = u"{name}" @@ -273,7 +273,7 @@ class Environment(BaseObject): dir_name, name_template.format(**_fact) ) - (serializer or self.serializer).write_to_file(fact_path, _fact) + (serializer or self.serializer).write_to_path(fact_path, _fact) return dir_name def read_deployment_info(self, fact_type, diff --git a/fuelclient/objects/node.py b/fuelclient/objects/node.py index 9782664..3ae5caf 100644 --- a/fuelclient/objects/node.py +++ b/fuelclient/objects/node.py @@ -93,7 +93,7 @@ class Node(BaseObject): ) if os.path.exists(attribute_path): os.remove(attribute_path) - return (serializer or self.serializer).write_to_file( + return (serializer or self.serializer).write_to_path( attribute_path, attributes ) diff --git a/fuelclient/objects/task.py b/fuelclient/objects/task.py index 98fdb10..388a5d8 100644 --- a/fuelclient/objects/task.py +++ b/fuelclient/objects/task.py @@ -95,6 +95,10 @@ class DeployTask(Task): class SnapshotTask(Task): @classmethod - def start_snapshot_task(cls): - dump_task = cls.connection.put_request("logs/package", {}) + def start_snapshot_task(cls, conf): + dump_task = cls.connection.put_request("logs/package", conf) return cls(dump_task["id"]) + + @classmethod + def get_default_config(cls): + return cls.connection.get_request("logs/package/config/default/") diff --git a/fuelclient/tests/test_snapshot.py b/fuelclient/tests/test_snapshot.py new file mode 100644 index 0000000..432f530 --- /dev/null +++ b/fuelclient/tests/test_snapshot.py @@ -0,0 +1,58 @@ +# 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 mock import call +from mock import patch + +from fuelclient.tests import base + + +class TestSnapshot(base.UnitTestCase): + + @patch('fuelclient.cli.actions.snapshot.SnapshotTask.get_default_config') + @patch('sys.stdout.write') + def test_get_default_config(self, mwrite, mconf): + + mconf.return_value = {'key': 'value'} + + self.execute(['fuel', 'snapshot', '--conf']) + self.assertEqual(mwrite.call_args_list, [call('key: value\n')]) + + @patch('fuelclient.cli.actions.snapshot.SnapshotTask.start_snapshot_task') + @patch('fuelclient.cli.actions.snapshot.' + 'download_snapshot_with_progress_bar') + @patch('sys.stdin') + def test_snapshot_with_provided_conf(self, mstdin, mbar, mstart): + conf = 'key: value\n' + + mstdin.isatty.return_value = False + mstdin.read.return_value = conf + + self.execute(['fuel', 'snapshot']) + + mstart.assert_called_once_with({'key': 'value'}) + self.assertEqual(mstdin.read.call_count, 1) + + @patch('fuelclient.cli.actions.snapshot.SnapshotTask.start_snapshot_task') + @patch('fuelclient.cli.actions.snapshot.' + 'download_snapshot_with_progress_bar') + @patch('sys.stdin') + def test_snapshot_without_conf(self, mstdin, mbar, mstart): + + mstdin.isatty.return_value = True + + self.execute(['fuel', 'snapshot']) + + mstart.assert_called_once_with({})