Add support for Audit Scope

This patch set adds audit scope support in python-watcherclient.

Change-Id: I2a94320bec0fd7051a38bee1c872148e39f69d0c
Partially-Implements: blueprint define-the-audit-scope
This commit is contained in:
Alexander Chadin 2016-09-02 12:08:12 +03:00
parent 11056302b4
commit c185f32ebb
11 changed files with 94 additions and 50 deletions

@ -11,3 +11,4 @@ pbr>=1.6 # Apache-2.0
PrettyTable<0.8,>=0.7.1 # BSD
python-keystoneclient!=2.1.0,>=2.0.0 # Apache-2.0
six>=1.9.0 # MIT
PyYAML>=3.10.0 # MIT

@ -43,4 +43,8 @@ class Lister(Command, lister.Lister):
class ShowOne(Command, show.ShowOne):
pass
def get_parser(self, prog_name, formatter_class=None):
parser = super(ShowOne, self).get_parser(prog_name)
if formatter_class:
parser.formatter_class = formatter_class
return parser

@ -19,7 +19,9 @@ from __future__ import print_function
import argparse
import json
import os
import uuid
import yaml
from oslo_utils import importutils
@ -192,3 +194,10 @@ def is_uuid_like(val):
return str(uuid.UUID(val)) == val
except (TypeError, ValueError, AttributeError):
return False
def serialize_file_to_dict(filename):
filename = os.path.expanduser(filename)
with open(filename, "rb") as stream:
scope = yaml.safe_load(stream.read())
return scope

@ -29,7 +29,6 @@ AUDIT_TEMPLATE_1 = {
'uuid': 'f8e47706-efcf-49a4-a5c4-af604eb492f2',
'name': 'at1',
'description': 'Audit Template 1 description',
'host_aggregate': 5,
'extra': {'automatic': False},
'goal_uuid': 'fc087747-61be-4aad-8126-b701731ae836',
'strategy_uuid': '2cf86250-d309-4b81-818e-1537f3dba6e5',
@ -64,7 +63,6 @@ AUDIT_1 = {
'state': 'PENDING',
'audit_template_uuid': 'f8e47706-efcf-49a4-a5c4-af604eb492f2',
'audit_template_name': 'at1',
'host_aggregate': 5,
'goal_name': 'SERVER_CONSOLIDATION',
'strategy_name': 'basic',
'created_at': datetime.datetime.now().isoformat(),
@ -72,6 +70,7 @@ AUDIT_1 = {
'deleted_at': None,
'parameters': None,
'interval': None,
'scope': '',
}
AUDIT_2 = {
@ -81,7 +80,6 @@ AUDIT_2 = {
'state': 'PENDING',
'audit_template_uuid': 'f8e47706-efcf-49a4-a5c4-af604eb492f2',
'audit_template_name': 'at1',
'host_aggregate': None,
'goal_name': 'fc087747-61be-4aad-8126-b701731ae836',
'strategy_name': None,
'created_at': datetime.datetime.now().isoformat(),
@ -89,6 +87,7 @@ AUDIT_2 = {
'deleted_at': None,
'parameters': None,
'interval': None,
'scope': '',
}
AUDIT_3 = {
@ -98,7 +97,6 @@ AUDIT_3 = {
'state': 'PENDING',
'audit_template_uuid': 'f8e47706-efcf-49a4-a5c4-af604eb492f2',
'audit_template_name': 'at1',
'host_aggregate': 3,
'goal_name': None,
'strategy_name': None,
'created_at': datetime.datetime.now().isoformat(),
@ -106,6 +104,7 @@ AUDIT_3 = {
'deleted_at': None,
'parameters': None,
'interval': 3600,
'scope': '',
}

@ -28,7 +28,6 @@ AUDIT_TMPL1 = {
'uuid': 'f8e47706-efcf-49a4-a5c4-af604eb492f2',
'name': 'Audit Template 1',
'description': 'Audit Template 1 description',
'host_aggregate': 5,
'extra': {'automatic': False},
'goal_uuid': '7568667b-51fe-4087-9eb1-29b26891036f',
'goal_name': 'SERVER_CONSOLIDATION',
@ -41,7 +40,6 @@ AUDIT_TMPL2 = {
'uuid': 'a5199d0e-0702-4613-9234-5ae2af8dafea',
'name': 'Audit Template 2',
'description': 'Audit Template 2 description',
'host_aggregate': 8,
'extra': {'automatic': True},
'goal_uuid': 'e75ee410-b32b-465f-88b5-4397705f9473',
'goal_name': 'DUMMY',
@ -54,7 +52,6 @@ AUDIT_TMPL3 = {
'uuid': '770ef053-ecb3-48b0-85b5-d55a2dbc6588',
'name': 'Audit Template 3',
'description': 'Audit Template 3 description',
'host_aggregate': 7,
'extra': {'automatic': True},
'goal_uuid': '7568667b-51fe-4087-9eb1-29b26891036f',
'goal_name': 'SERVER_CONSOLIDATION',
@ -407,8 +404,6 @@ class AuditTemplateManagerTest(utils.BaseTestCase):
self.assertEqual(AUDIT_TMPL1['name'], audit_template.name)
self.assertEqual(AUDIT_TMPL1['description'],
audit_template.description)
self.assertEqual(AUDIT_TMPL1['host_aggregate'],
audit_template.host_aggregate)
self.assertEqual(AUDIT_TMPL1['goal_uuid'], audit_template.goal_uuid)
self.assertEqual(AUDIT_TMPL1['strategy_uuid'],
audit_template.strategy_uuid)
@ -427,8 +422,6 @@ class AuditTemplateManagerTest(utils.BaseTestCase):
self.assertEqual(AUDIT_TMPL1['name'], audit_template.name)
self.assertEqual(AUDIT_TMPL1['description'],
audit_template.description)
self.assertEqual(AUDIT_TMPL1['host_aggregate'],
audit_template.host_aggregate)
self.assertEqual(AUDIT_TMPL1['goal_uuid'], audit_template.goal_uuid)
self.assertEqual(AUDIT_TMPL1['strategy_uuid'],
audit_template.strategy_uuid)

@ -47,7 +47,6 @@ AUDIT_TEMPLATE_1 = {
'uuid': 'f8e47706-efcf-49a4-a5c4-af604eb492f2',
'name': 'at1',
'description': 'Audit Template 1 description',
'host_aggregate': 5,
'extra': {'automatic': False},
'goal_uuid': 'fc087747-61be-4aad-8126-b701731ae836',
'goal_name': 'SERVER_CONSOLIDATION',
@ -56,13 +55,13 @@ AUDIT_TEMPLATE_1 = {
'created_at': datetime.datetime.now().isoformat(),
'updated_at': None,
'deleted_at': None,
'scope': []
}
AUDIT_TEMPLATE_2 = {
'uuid': '2a60ca9b-09b0-40ff-8674-de8a36fc4bc8',
'name': 'at2',
'description': 'Audit Template 2',
'host_aggregate': 3,
'extra': {'automatic': False},
'goal_uuid': 'fc087747-61be-4aad-8126-b701731ae836',
'goal_name': 'SERVER_CONSOLIDATION',
@ -71,6 +70,7 @@ AUDIT_TEMPLATE_2 = {
'created_at': datetime.datetime.now().isoformat(),
'updated_at': None,
'deleted_at': None,
'scope': []
}
@ -299,7 +299,7 @@ class AuditTemplateShellTest(base.CommandTestCase):
self.m_audit_template_mgr.update.return_value = audit_template
exit_code, result = self.run_cmd(
'audittemplate update at1 replace host_aggregate=5')
'audittemplate update at1 replace description="New description"')
self.assertEqual(0, exit_code)
self.assertEqual(self.resource_as_dict(audit_template, self.FIELDS,
@ -307,7 +307,8 @@ class AuditTemplateShellTest(base.CommandTestCase):
result)
self.m_audit_template_mgr.update.assert_called_once_with(
'at1',
[{'op': 'replace', 'path': '/host_aggregate', 'value': 5}])
[{'op': 'replace', 'path': '/description',
'value': 'New description'}])
def test_do_audit_template_create(self):
audit_template = resource.AuditTemplate(mock.Mock(), AUDIT_TEMPLATE_1)
@ -346,8 +347,7 @@ class AuditTemplateShellTest(base.CommandTestCase):
self.m_audit_template_mgr.create.return_value = audit_template
exit_code, result = self.run_cmd(
'audittemplate create at1 fc087747-61be-4aad-8126-b701731ae836 '
'-a 5')
'audittemplate create at1 fc087747-61be-4aad-8126-b701731ae836')
self.assertEqual(0, exit_code)
self.assertEqual(self.resource_as_dict(audit_template, self.FIELDS,
@ -355,8 +355,7 @@ class AuditTemplateShellTest(base.CommandTestCase):
result)
self.m_audit_template_mgr.create.assert_called_once_with(
goal='fc087747-61be-4aad-8126-b701731ae836',
name='at1',
host_aggregate='5')
name='at1')
def test_do_audit_template_create_with_extra(self):
audit_template = resource.AuditTemplate(mock.Mock(), AUDIT_TEMPLATE_1)

@ -19,9 +19,8 @@ from watcherclient.common import utils
from watcherclient import exceptions as exc
CREATION_ATTRIBUTES = ['audit_template_uuid', 'host_aggregate',
'deadline', 'audit_type', 'interval',
'parameters', 'goal', 'strategy']
CREATION_ATTRIBUTES = ['audit_template_uuid', 'deadline', 'audit_type',
'interval', 'parameters', 'goal', 'strategy']
class Audit(base.Resource):

@ -162,12 +162,6 @@ class CreateAudit(command.ShowOne):
dest='strategy',
metavar='<strategy>',
help=_('Strategy UUID or name associated to this audit.'))
parser.add_argument(
'-r', '--host-aggregate',
dest='host_aggregate',
metavar='<host-aggregate>',
help=_('Name or UUID of the host aggregate targeted '
'by this audit.'))
parser.add_argument(
'-a', '--audit-template',
dest='audit_template_uuid',
@ -178,9 +172,8 @@ class CreateAudit(command.ShowOne):
def take_action(self, parsed_args):
client = getattr(self.app.client_manager, "infra-optim")
field_list = ['audit_template_uuid', 'host_aggregate',
'audit_type', 'deadline', 'parameters', 'interval',
'goal', 'strategy']
field_list = ['audit_template_uuid', 'audit_type', 'deadline',
'parameters', 'interval', 'goal', 'strategy']
fields = dict((k, v) for (k, v) in vars(parsed_args).items()
if k in field_list and v is not None)

@ -18,8 +18,8 @@ from watcherclient.common import base
from watcherclient.common import utils
from watcherclient import exceptions as exc
CREATION_ATTRIBUTES = ['host_aggregate', 'description', 'name',
'extra', 'goal', 'strategy']
CREATION_ATTRIBUTES = ['description', 'name', 'extra', 'goal', 'strategy',
'scope']
class AuditTemplate(base.Resource):

@ -14,6 +14,8 @@
# See the License for the specific language governing permissions and
# limitations under the License.
import argparse
from osc_lib import utils
from oslo_utils import uuidutils
@ -126,7 +128,17 @@ class CreateAuditTemplate(command.ShowOne):
"""Create new audit template."""
def get_parser(self, prog_name):
parser = super(CreateAuditTemplate, self).get_parser(prog_name)
class SmartFormatter(argparse.HelpFormatter):
def _split_lines(self, text, width):
if '\n' in text:
return text.splitlines()
else:
return argparse.HelpFormatter._split_lines(
self, text, width)
parser = super(CreateAuditTemplate, self).get_parser(
prog_name, formatter_class=SmartFormatter)
parser.add_argument(
'name',
metavar='<name>',
@ -151,19 +163,53 @@ class CreateAuditTemplate(command.ShowOne):
help=_("Record arbitrary key/value metadata. "
"Can be specified multiple times."))
parser.add_argument(
'-a', '--host-aggregate',
dest='host_aggregate',
metavar='<host-aggregate>',
help=_('Name or UUID of the host aggregate targeted '
'by this audit template.'))
'--scope',
metavar='<path>',
help=_("Part of the cluster on which an audit will be done.\n"
"Can be provided either in yaml or json file.\n"
"YAML example:\n"
"---\n"
" - host_aggregates:\n"
" - id: 1\n"
" - id: 2\n"
" - id: 3\n"
" - availability_zones:\n"
" - name: AZ1\n"
" - name: AZ2\n"
" - exclude:\n"
" - instances:\n"
" - uuid: UUID1\n"
" - uuid: UUID2\n"
" - compute_nodes:\n"
" - name: compute1\n"
"\n"
"JSON example:\n"
"[{'host_aggregates': [\n"
" {'id': 1},\n"
" {'id': 2},\n"
" {'id': 3}]},\n"
" {'availability_zones': [\n"
" {'name': 'AZ1'},\n"
" {'name': 'AZ2'}]},\n"
" {'exclude': [\n"
" {'instances': [\n"
" {'uuid': 'UUID1'},\n"
" {'uuid': 'UUID2'}\n"
" ]},\n"
" {'compute_nodes': [\n"
" {'name': 'compute1'}\n"
" ]}\n"
"]}]\n"
)
)
return parser
def take_action(self, parsed_args):
client = getattr(self.app.client_manager, "infra-optim")
field_list = ['host_aggregate', 'description', 'name', 'extra',
'goal', 'strategy']
field_list = ['description', 'name', 'extra', 'goal', 'strategy',
'scope']
fields = dict((k, v) for (k, v) in vars(parsed_args).items()
if k in field_list and v is not None)
@ -176,6 +222,9 @@ class CreateAuditTemplate(command.ShowOne):
if not uuidutils.is_uuid_like(fields['strategy']):
fields['strategy'] = client.strategy.get(
fields['strategy']).uuid
if fields.get('scope'):
fields['scope'] = common_utils.serialize_file_to_dict(
fields['scope'])
fields = common_utils.args_array_to_dict(fields, 'extra')
audit_template = client.audit_template.create(**fields)

@ -19,13 +19,11 @@
# Audit Template
AUDIT_TEMPLATE_FIELDS = [
'uuid', 'created_at', 'updated_at', 'deleted_at',
'description', 'host_aggregate', 'name',
'extra', 'goal_name', 'strategy_name']
'description', 'name', 'extra', 'goal_name', 'strategy_name', 'scope']
AUDIT_TEMPLATE_FIELD_LABELS = [
'UUID', 'Created At', 'Updated At', 'Deleted At',
'Description', 'Host Aggregate ID or Name', 'Name',
'Extra', 'Goal', 'Strategy']
'Description', 'Name', 'Extra', 'Goal', 'Strategy', 'Audit Scope']
AUDIT_TEMPLATE_SHORT_LIST_FIELDS = [
'uuid', 'name', 'goal_name', 'strategy_name']
@ -35,13 +33,13 @@ AUDIT_TEMPLATE_SHORT_LIST_FIELD_LABELS = ['UUID', 'Name', 'Goal', 'Strategy']
# Audit
AUDIT_FIELDS = ['uuid', 'created_at', 'updated_at', 'deleted_at',
'deadline', 'state', 'audit_type',
'parameters', 'interval',
'host_aggregate', 'goal_name', 'strategy_name']
'parameters', 'interval', 'goal_name', 'strategy_name',
'scope']
AUDIT_FIELD_LABELS = ['UUID', 'Created At', 'Updated At', 'Deleted At',
'Deadline', 'State', 'Audit Type',
'Parameters', 'Interval', 'Host Aggregate ID or Name',
'Goal', 'Strategy']
'Parameters', 'Interval', 'Goal', 'Strategy',
'Audit Scope']
AUDIT_SHORT_LIST_FIELDS = ['uuid', 'audit_type',
'state', 'goal_name', 'strategy_name']