Add commands to operate snapshot config

Added two commands to dump snapshot config
and provide it for snapshot task

Will download default config and dump it to stdout

fuel snapshot --conf > conf.yaml

Next command will accept config as input and bypass it to snapshot
generation request

fuel snapshot < conf.yaml

This is required to overwrite certain parameters if needed,
some of those are:
- target directory of snapshot generation
- timeout of snapshot generation procedure
- exclusion/inclusion of certain directories

Depends on I5e01a7459cefe49a128192d82dd827f02866f909
Related-Bug: 1382511
Related-Bug: 1420054
DocImpact

Change-Id: I1ddab26fba1346dad30955289e7d28f4d3aa1562
This commit is contained in:
Dmitry Shulyak
2015-02-20 14:08:51 +02:00
parent 8a292dbdfc
commit bb5434f0cf
8 changed files with 110 additions and 13 deletions

View File

@@ -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:

View File

@@ -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:

View File

@@ -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)

View File

@@ -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):

View File

@@ -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,

View File

@@ -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
)

View File

@@ -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/")

View File

@@ -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({})