Add fuel2 diagnostic snapshot commands
This patch adds commands that allow to manipulate with diagnostic snapshot process creation: fuel2 snapshot get-default-config fuel2 snapshot create fuel2 snapshot get-link DocImpact Change-Id: I59542eba922d793406c50b864b1cd0c416c7c891
This commit is contained in:
parent
82946ddbf9
commit
5e5edefe24
|
@ -72,6 +72,7 @@ def get_client(resource, version='v1', connection=None):
|
|||
'openstack-config': v1.openstack_config,
|
||||
'plugins': v1.plugins,
|
||||
'release': v1.release,
|
||||
'snapshot': v1.snapshot,
|
||||
'task': v1.task,
|
||||
'vip': v1.vip
|
||||
}
|
||||
|
|
|
@ -0,0 +1,132 @@
|
|||
# -*- 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 argparse
|
||||
import os
|
||||
|
||||
from oslo_utils import fileutils
|
||||
|
||||
from fuelclient.cli import error
|
||||
from fuelclient.commands import base
|
||||
from fuelclient.common import data_utils
|
||||
from fuelclient import utils
|
||||
|
||||
|
||||
class SnapshotMixIn(object):
|
||||
|
||||
entity_name = 'snapshot'
|
||||
supported_file_formats = ('json', 'yaml')
|
||||
|
||||
@staticmethod
|
||||
def config_file(file_path):
|
||||
if not utils.file_exists(file_path):
|
||||
raise argparse.ArgumentTypeError(
|
||||
'File "{0}" does not exist'.format(file_path))
|
||||
return file_path
|
||||
|
||||
@staticmethod
|
||||
def get_config_path(directory, file_format):
|
||||
return os.path.join(os.path.abspath(directory),
|
||||
'snapshot_conf.{}'.format(file_format))
|
||||
|
||||
|
||||
class SnapshotGenerate(SnapshotMixIn, base.BaseCommand):
|
||||
"""Generate diagnostic snapshot."""
|
||||
|
||||
def get_parser(self, prog_name):
|
||||
parser = super(SnapshotGenerate, self).get_parser(prog_name)
|
||||
parser.add_argument('-c',
|
||||
'--config',
|
||||
required=False,
|
||||
type=self.config_file,
|
||||
help='Configuration file.')
|
||||
return parser
|
||||
|
||||
def take_action(self, parsed_args):
|
||||
file_path = parsed_args.config
|
||||
|
||||
config = dict()
|
||||
if file_path:
|
||||
file_format = os.path.splitext(file_path)[1].lstrip('.')
|
||||
try:
|
||||
with open(file_path, 'r') as stream:
|
||||
config = data_utils.safe_load(file_format, stream)
|
||||
except (OSError, IOError):
|
||||
msg = 'Could not read configuration at {}.'
|
||||
raise error.InvalidFileException(msg.format(file_path))
|
||||
|
||||
result = self.client.create_snapshot(config)
|
||||
|
||||
msg = "Diagnostic snapshot generation task with id {id} was started\n"
|
||||
self.app.stdout.write(msg.format(id=result.id))
|
||||
|
||||
|
||||
class SnapshotConfigGetDefault(SnapshotMixIn, base.BaseCommand):
|
||||
"""Download default config to generate custom diagnostic snapshot."""
|
||||
|
||||
def get_parser(self, prog_name):
|
||||
parser = super(SnapshotConfigGetDefault, self).get_parser(prog_name)
|
||||
parser.add_argument('-f',
|
||||
'--format',
|
||||
required=True,
|
||||
choices=self.supported_file_formats,
|
||||
help='Format of serialized diagnostic snapshot '
|
||||
'configuration data.')
|
||||
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):
|
||||
file_path = self.get_config_path(parsed_args.directory,
|
||||
parsed_args.format)
|
||||
config = self.client.get_default_config()
|
||||
|
||||
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, config)
|
||||
except (OSError, IOError):
|
||||
msg = 'Could not store configuration at {}.'
|
||||
raise error.InvalidFileException(msg.format(file_path))
|
||||
|
||||
msg = "Configuration was stored in {path}\n"
|
||||
self.app.stdout.write(msg.format(path=file_path))
|
||||
|
||||
|
||||
class SnapshotGetLink(SnapshotMixIn, base.BaseShowCommand):
|
||||
"""Show link to download diagnostic snapshot."""
|
||||
|
||||
columns = ('status',
|
||||
'link')
|
||||
|
||||
def take_action(self, parsed_args):
|
||||
data = self.client.get_by_id(parsed_args.id)
|
||||
if data['name'] != 'dump':
|
||||
msg = "Task with id {0} is not a snapshot generation task"
|
||||
raise error.ActionException(msg.format(data['id']))
|
||||
if data['status'] != 'ready':
|
||||
data['link'] = None
|
||||
else:
|
||||
data['link'] = self.client.connection.root + data['message']
|
||||
|
||||
data = data_utils.get_display_data_single(self.columns, data)
|
||||
return self.columns, data
|
|
@ -0,0 +1,131 @@
|
|||
# -*- 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.cli import error
|
||||
from fuelclient.tests.unit.v2.cli import test_engine
|
||||
|
||||
|
||||
class TestSnapshotCommand(test_engine.BaseCLITest):
|
||||
|
||||
@mock.patch('json.dump')
|
||||
def test_snapshot_config_download_json(self, m_dump):
|
||||
args = 'snapshot get-default-config -f json -d /tmp'
|
||||
test_data = {'foo': 'bar'}
|
||||
expected_path = '/tmp/snapshot_conf.json'
|
||||
|
||||
self.m_client.get_default_config.return_value = test_data
|
||||
|
||||
m_open = mock.mock_open()
|
||||
with mock.patch('fuelclient.commands.snapshot.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('snapshot', mock.ANY)
|
||||
self.m_client.get_default_config.assert_called_once_with()
|
||||
|
||||
@mock.patch('yaml.safe_dump')
|
||||
def test_snapshot_config_download_yaml(self, m_safe_dump):
|
||||
args = 'snapshot get-default-config -f yaml -d /tmp'
|
||||
test_data = {'foo': 'bar'}
|
||||
expected_path = '/tmp/snapshot_conf.yaml'
|
||||
|
||||
self.m_client.get_default_config.return_value = test_data
|
||||
|
||||
m_open = mock.mock_open()
|
||||
with mock.patch('fuelclient.commands.snapshot.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('snapshot', mock.ANY)
|
||||
self.m_client.get_default_config.assert_called_once_with()
|
||||
|
||||
def test_snapshot_create(self):
|
||||
args = 'snapshot create'
|
||||
test_data = {}
|
||||
self.exec_command(args)
|
||||
|
||||
self.m_get_client.assert_called_once_with('snapshot', mock.ANY)
|
||||
self.m_client.create_snapshot.assert_called_once_with(test_data)
|
||||
|
||||
@mock.patch('fuelclient.utils.file_exists', mock.Mock(return_value=True))
|
||||
def test_snapshot_create_w_config_json(self):
|
||||
args = 'snapshot create -c /tmp/snapshot_conf.json'
|
||||
test_data = {'foo': 'bar'}
|
||||
expected_path = '/tmp/snapshot_conf.json'
|
||||
|
||||
m_open = mock.mock_open(read_data=json.dumps(test_data))
|
||||
with mock.patch('fuelclient.commands.snapshot.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('snapshot', mock.ANY)
|
||||
self.m_client.create_snapshot.assert_called_once_with(test_data)
|
||||
|
||||
@mock.patch('fuelclient.utils.file_exists', mock.Mock(return_value=True))
|
||||
def test_snapshot_create_w_config_yaml(self):
|
||||
args = 'snapshot create -c /tmp/snapshot_conf.yaml'
|
||||
test_data = {'foo': 'bar'}
|
||||
expected_path = '/tmp/snapshot_conf.yaml'
|
||||
|
||||
m_open = mock.mock_open(read_data=yaml.dump(test_data))
|
||||
with mock.patch('fuelclient.commands.snapshot.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('snapshot', mock.ANY)
|
||||
self.m_client.create_snapshot.assert_called_once_with(test_data)
|
||||
|
||||
def test_snapshot_get_link(self):
|
||||
task_id = 45
|
||||
args = 'snapshot get-link {}'.format(task_id)
|
||||
test_data = {'id': task_id,
|
||||
'name': 'dump',
|
||||
'status': 'ready',
|
||||
'message': 'fake_message'}
|
||||
|
||||
self.m_client.get_by_id.return_value = test_data
|
||||
|
||||
self.exec_command(args)
|
||||
self.m_get_client.assert_called_once_with('snapshot', mock.ANY)
|
||||
self.m_client.get_by_id.assert_called_once_with(task_id)
|
||||
|
||||
@mock.patch('sys.stderr')
|
||||
def test_snapshot_get_link_fail(self, mocked_stderr):
|
||||
task_id = 45
|
||||
args = 'snapshot get-link {}'.format(task_id)
|
||||
test_data = {'id': task_id,
|
||||
'name': 'not_dump_name',
|
||||
'status': 'ready',
|
||||
'message': 'fake_message'}
|
||||
|
||||
self.m_client.get_by_id.return_value = test_data
|
||||
|
||||
self.assertRaises(error.ActionException, self.exec_command, args)
|
||||
self.assertIn('Task with id {} is not a snapshot generation '
|
||||
'task'.format(task_id),
|
||||
mocked_stderr.write.call_args_list[0][0][0])
|
|
@ -0,0 +1,61 @@
|
|||
# -*- 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 TestSnapshotFacade(test_api.BaseLibTest):
|
||||
|
||||
def setUp(self):
|
||||
super(TestSnapshotFacade, self).setUp()
|
||||
|
||||
self.version = 'v1'
|
||||
self.res_uri = '/api/{version}/logs/package'.format(
|
||||
version=self.version)
|
||||
self.client = fuelclient.get_client('snapshot', self.version)
|
||||
self.fake_task = utils.get_fake_task()
|
||||
|
||||
def test_snapshot_config_download(self):
|
||||
fake_resp = {'test_key': 'test_value'}
|
||||
expected_uri = '/api/{version}/logs/package/config/default/'.format(
|
||||
version=self.version)
|
||||
|
||||
matcher = self.m_request.get(expected_uri, json=fake_resp)
|
||||
|
||||
conf = self.client.get_default_config()
|
||||
self.assertTrue(matcher.called)
|
||||
self.assertEqual(conf, fake_resp)
|
||||
|
||||
def test_snapshot_create(self):
|
||||
fake_config = {}
|
||||
matcher = self.m_request.put(self.res_uri, json=self.fake_task)
|
||||
|
||||
self.client.create_snapshot(fake_config)
|
||||
|
||||
self.assertTrue(matcher.called)
|
||||
self.assertEqual(fake_config, matcher.last_request.json())
|
||||
|
||||
def test_snapshot_create_w_config(self):
|
||||
fake_config = {'key_value': 'data_value'}
|
||||
|
||||
matcher = self.m_request.put(self.res_uri, json=self.fake_task)
|
||||
|
||||
self.client.create_snapshot(fake_config)
|
||||
|
||||
self.assertTrue(matcher.called)
|
||||
self.assertEqual(fake_config, matcher.last_request.json())
|
|
@ -24,6 +24,7 @@ from fuelclient.v1 import node
|
|||
from fuelclient.v1 import openstack_config
|
||||
from fuelclient.v1 import release
|
||||
from fuelclient.v1 import plugins
|
||||
from fuelclient.v1 import snapshot
|
||||
from fuelclient.v1 import task
|
||||
from fuelclient.v1 import vip
|
||||
|
||||
|
@ -40,5 +41,6 @@ __all__ = ('cluster_settings',
|
|||
'openstack_config',
|
||||
'plugins',
|
||||
'release',
|
||||
'snapshot',
|
||||
'task',
|
||||
'vip')
|
||||
|
|
|
@ -0,0 +1,33 @@
|
|||
# -*- 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 SnapshotClient(base_v1.BaseV1Client):
|
||||
|
||||
_entity_wrapper = objects.SnapshotTask
|
||||
|
||||
def create_snapshot(self, config):
|
||||
return self._entity_wrapper.start_snapshot_task(config)
|
||||
|
||||
def get_default_config(self):
|
||||
return self._entity_wrapper.get_default_config()
|
||||
|
||||
|
||||
def get_client(connection):
|
||||
return SnapshotClient(connection)
|
|
@ -103,6 +103,9 @@ fuelclient =
|
|||
task_network-configuration_download=fuelclient.commands.task:TaskNetworkConfigurationDownload
|
||||
task_settings_download=fuelclient.commands.task:TaskClusterSettingsDownload
|
||||
task_show=fuelclient.commands.task:TaskShow
|
||||
snapshot_create=fuelclient.commands.snapshot:SnapshotGenerate
|
||||
snapshot_get-default-config=fuelclient.commands.snapshot:SnapshotConfigGetDefault
|
||||
snapshot_get-link=fuelclient.commands.snapshot:SnapshotGetLink
|
||||
vip_create=fuelclient.commands.vip:VipCreate
|
||||
vip_download=fuelclient.commands.vip:VipDownload
|
||||
vip_upload=fuelclient.commands.vip:VipUpload
|
||||
|
|
Loading…
Reference in New Issue