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

View File

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

View File

@@ -43,4 +43,8 @@ class Lister(Command, lister.Lister):
class ShowOne(Command, show.ShowOne): 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

View File

@@ -19,7 +19,9 @@ from __future__ import print_function
import argparse import argparse
import json import json
import os
import uuid import uuid
import yaml
from oslo_utils import importutils from oslo_utils import importutils
@@ -192,3 +194,10 @@ def is_uuid_like(val):
return str(uuid.UUID(val)) == val return str(uuid.UUID(val)) == val
except (TypeError, ValueError, AttributeError): except (TypeError, ValueError, AttributeError):
return False 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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -14,6 +14,8 @@
# See the License for the specific language governing permissions and # See the License for the specific language governing permissions and
# limitations under the License. # limitations under the License.
import argparse
from osc_lib import utils from osc_lib import utils
from oslo_utils import uuidutils from oslo_utils import uuidutils
@@ -126,7 +128,17 @@ class CreateAuditTemplate(command.ShowOne):
"""Create new audit template.""" """Create new audit template."""
def get_parser(self, prog_name): 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( parser.add_argument(
'name', 'name',
metavar='<name>', metavar='<name>',
@@ -151,19 +163,53 @@ class CreateAuditTemplate(command.ShowOne):
help=_("Record arbitrary key/value metadata. " help=_("Record arbitrary key/value metadata. "
"Can be specified multiple times.")) "Can be specified multiple times."))
parser.add_argument( parser.add_argument(
'-a', '--host-aggregate', '--scope',
dest='host_aggregate', metavar='<path>',
metavar='<host-aggregate>', help=_("Part of the cluster on which an audit will be done.\n"
help=_('Name or UUID of the host aggregate targeted ' "Can be provided either in yaml or json file.\n"
'by this audit template.')) "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 return parser
def take_action(self, parsed_args): def take_action(self, parsed_args):
client = getattr(self.app.client_manager, "infra-optim") client = getattr(self.app.client_manager, "infra-optim")
field_list = ['host_aggregate', 'description', 'name', 'extra', field_list = ['description', 'name', 'extra', 'goal', 'strategy',
'goal', 'strategy'] 'scope']
fields = dict((k, v) for (k, v) in vars(parsed_args).items() fields = dict((k, v) for (k, v) in vars(parsed_args).items()
if k in field_list and v is not None) 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']): if not uuidutils.is_uuid_like(fields['strategy']):
fields['strategy'] = client.strategy.get( fields['strategy'] = client.strategy.get(
fields['strategy']).uuid 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') fields = common_utils.args_array_to_dict(fields, 'extra')
audit_template = client.audit_template.create(**fields) audit_template = client.audit_template.create(**fields)

View File

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