Initial congressclient
This patch adds client support for congress with keystone and supports the following api commands: $ openstack congress policy list $ openstack congress policy row get $ openstack congress policy rule create $ openstack congress policy rule delete $ openstack congress policy rules list Change-Id: I624b54f6cf2614eaec970c8bdd3766291dcc9489
This commit is contained in:
parent
d44349d9f0
commit
c0612c0c6c
0
congressclient/common/__init__.py
Normal file
0
congressclient/common/__init__.py
Normal file
94
congressclient/common/utils.py
Normal file
94
congressclient/common/utils.py
Normal file
@ -0,0 +1,94 @@
|
||||
# Copyright 2012-2013 OpenStack, LLC.
|
||||
#
|
||||
# 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 os
|
||||
import sys
|
||||
|
||||
from congressclient import exceptions
|
||||
|
||||
|
||||
def env(*vars, **kwargs):
|
||||
"""Search for the first defined of possibly many env vars
|
||||
|
||||
Returns the first environment variable defined in vars, or
|
||||
returns the default defined in kwargs.
|
||||
"""
|
||||
for v in vars:
|
||||
value = os.environ.get(v, None)
|
||||
if value:
|
||||
return value
|
||||
return kwargs.get('default', '')
|
||||
|
||||
|
||||
def get_client_class(api_name, version, version_map):
|
||||
"""Returns the client class for the requested API version
|
||||
|
||||
:param api_name: the name of the API, e.g. 'compute', 'image', etc
|
||||
:param version: the requested API version
|
||||
:param version_map: a dict of client classes keyed by version
|
||||
:rtype: a client class for the requested API version
|
||||
"""
|
||||
try:
|
||||
client_path = version_map[str(version)]
|
||||
except (KeyError, ValueError):
|
||||
msg = "Invalid %s client version '%s'. must be one of: %s" % (
|
||||
(api_name, version, ', '.join(version_map.keys())))
|
||||
raise exceptions.UnsupportedVersion(msg)
|
||||
|
||||
return import_class(client_path)
|
||||
|
||||
|
||||
def import_class(import_str):
|
||||
"""Returns a class from a string including module and class
|
||||
|
||||
:param import_str: a string representation of the class name
|
||||
:rtype: the requested class
|
||||
"""
|
||||
mod_str, _sep, class_str = import_str.rpartition('.')
|
||||
__import__(mod_str)
|
||||
return getattr(sys.modules[mod_str], class_str)
|
||||
|
||||
|
||||
def format_list(data):
|
||||
"""Return a formatted strings
|
||||
|
||||
:param data: a list of strings
|
||||
:rtype: a string formatted to a,b,c
|
||||
"""
|
||||
|
||||
return ', '.join(data)
|
||||
|
||||
|
||||
def get_dict_properties(item, fields, mixed_case_fields=[], formatters={}):
|
||||
"""Return a tuple containing the item properties.
|
||||
|
||||
:param item: a single dict resource
|
||||
:param fields: tuple of strings with the desired field names
|
||||
:param mixed_case_fields: tuple of field names to preserve case
|
||||
:param formatters: dictionary mapping field names to callables
|
||||
to format the values
|
||||
"""
|
||||
row = []
|
||||
|
||||
for field in fields:
|
||||
if field in mixed_case_fields:
|
||||
field_name = field.replace(' ', '_')
|
||||
else:
|
||||
field_name = field.lower().replace(' ', '_')
|
||||
data = item[field_name] if field_name in item else ''
|
||||
if field in formatters:
|
||||
row.append(formatters[field](data))
|
||||
else:
|
||||
row.append(data)
|
||||
return tuple(row)
|
16
congressclient/exceptions.py
Normal file
16
congressclient/exceptions.py
Normal file
@ -0,0 +1,16 @@
|
||||
# Copyright (c) 2012 OpenStack Foundation.
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# 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 congressclient.openstack.common.apiclient.exceptions import * # noqa
|
0
congressclient/osc/__init__.py
Normal file
0
congressclient/osc/__init__.py
Normal file
61
congressclient/osc/osc_plugin.py
Normal file
61
congressclient/osc/osc_plugin.py
Normal file
@ -0,0 +1,61 @@
|
||||
# Copyright 2014 VMWare.
|
||||
#
|
||||
# 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 logging
|
||||
|
||||
import keystoneclient
|
||||
|
||||
from congressclient.common import utils
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
DEFAULT_COMPUTE_API_VERSION = '1'
|
||||
API_VERSION_OPTION = 'os_policy_api_version'
|
||||
API_NAME = 'congressclient'
|
||||
API_VERSIONS = {
|
||||
'1': 'congressclient.v1.client.Client',
|
||||
}
|
||||
|
||||
|
||||
def make_client(instance):
|
||||
"""Returns a congress service client."""
|
||||
congress_client = utils.get_client_class(
|
||||
API_NAME,
|
||||
instance._api_version[API_NAME],
|
||||
API_VERSIONS)
|
||||
auth = keystoneclient.auth.identity.v2.Password(
|
||||
auth_url=instance._auth_url,
|
||||
username=instance._username,
|
||||
password=instance._password, tenant_name=instance._project_name)
|
||||
session = keystoneclient.session.Session(auth=auth)
|
||||
LOG.debug('instantiating congress client: %s', congress_client)
|
||||
return congress_client(session=session,
|
||||
auth=None,
|
||||
interface='publicURL',
|
||||
service_type='policy',
|
||||
region_name=instance._region_name)
|
||||
|
||||
|
||||
def build_option_parser(parser):
|
||||
"""Hook to add global options."""
|
||||
parser.add_argument(
|
||||
'--os-policy-api-version',
|
||||
metavar='<policy-api-version>',
|
||||
default=utils.env(
|
||||
'OS_POLICY_API_VERSION',
|
||||
default=DEFAULT_COMPUTE_API_VERSION),
|
||||
help='Policy API version, default=' +
|
||||
DEFAULT_COMPUTE_API_VERSION +
|
||||
' (Env: OS_POLICY_API_VERSION)')
|
||||
return parser
|
0
congressclient/osc/v1/__init__.py
Normal file
0
congressclient/osc/v1/__init__.py
Normal file
162
congressclient/osc/v1/policy.py
Normal file
162
congressclient/osc/v1/policy.py
Normal file
@ -0,0 +1,162 @@
|
||||
# Copyright 2012-2013 OpenStack, LLC.
|
||||
#
|
||||
# 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.
|
||||
|
||||
"""Policy action implemenations"""
|
||||
|
||||
import logging
|
||||
|
||||
from cliff import command
|
||||
from cliff import lister
|
||||
from cliff import show
|
||||
import six
|
||||
|
||||
from congressclient.common import utils
|
||||
from congressclient.openstack.common import jsonutils
|
||||
|
||||
|
||||
def _format_rule(rule):
|
||||
"""Break up rule string so it fits on screen."""
|
||||
|
||||
rule_split = jsonutils.dumps(rule).split(":-")
|
||||
formatted_string = rule_split[0] + ":-\n"
|
||||
for rule in rule_split[1].split("), "):
|
||||
formatted_string += rule + '\n'
|
||||
return formatted_string
|
||||
|
||||
|
||||
class CreatePolicyRule(show.ShowOne):
|
||||
"""Create a policy rule."""
|
||||
|
||||
log = logging.getLogger(__name__ + '.CreatePolicyRule')
|
||||
|
||||
def get_parser(self, prog_name):
|
||||
parser = super(CreatePolicyRule, self).get_parser(prog_name)
|
||||
parser.add_argument(
|
||||
'policy_name',
|
||||
metavar="<policy-name>",
|
||||
help="Name or identifier of the policy")
|
||||
parser.add_argument(
|
||||
'rule',
|
||||
metavar="<rule>",
|
||||
help="Policy rule")
|
||||
|
||||
return parser
|
||||
|
||||
def take_action(self, parsed_args):
|
||||
self.log.debug('take_action(%s)' % parsed_args)
|
||||
client = self.app.client_manager.congressclient
|
||||
body = {'rule': parsed_args.rule}
|
||||
data = client.create_policy_rule(parsed_args.policy_name, body)
|
||||
data['rule'] = _format_rule(data['rule'])
|
||||
return zip(*sorted(six.iteritems(data)))
|
||||
|
||||
|
||||
class DeletePolicyRule(command.Command):
|
||||
"""Delete a policy rule."""
|
||||
|
||||
log = logging.getLogger(__name__ + '.DeletePolicyRule')
|
||||
|
||||
def get_parser(self, prog_name):
|
||||
parser = super(DeletePolicyRule, self).get_parser(prog_name)
|
||||
parser.add_argument(
|
||||
'policy_name',
|
||||
metavar="<policy-name>",
|
||||
help="Name of the policy to delete")
|
||||
parser.add_argument(
|
||||
'rule_id',
|
||||
metavar="<rule-id>",
|
||||
help="ID of the policy to delete")
|
||||
return parser
|
||||
|
||||
def take_action(self, parsed_args):
|
||||
self.log.debug('take_action(%s)' % parsed_args)
|
||||
client = self.app.client_manager.congressclient
|
||||
client.delete_policy_rule(parsed_args.policy_name,
|
||||
parsed_args.rule_id)
|
||||
|
||||
|
||||
class ListPolicyRules(lister.Lister):
|
||||
"""List policy rules."""
|
||||
|
||||
log = logging.getLogger(__name__ + '.ListPolicyRules')
|
||||
|
||||
def get_parser(self, prog_name):
|
||||
parser = super(ListPolicyRules, self).get_parser(prog_name)
|
||||
parser.add_argument(
|
||||
'policy_name',
|
||||
metavar="<policy-name>",
|
||||
help="Name of the policy")
|
||||
return parser
|
||||
|
||||
def take_action(self, parsed_args):
|
||||
self.log.debug('take_action(%s)' % parsed_args)
|
||||
client = self.app.client_manager.congressclient
|
||||
data = client.list_policy_rules(parsed_args.policy_name)['results']
|
||||
columns = ['id', 'comment', 'rule']
|
||||
formatters = {'PolicyRules': utils.format_list}
|
||||
return (columns,
|
||||
(utils.get_dict_properties(s, columns,
|
||||
formatters=formatters)
|
||||
for s in data))
|
||||
|
||||
|
||||
class ListPolicy(lister.Lister):
|
||||
"""List Policy."""
|
||||
|
||||
log = logging.getLogger(__name__ + '.ListPolicy')
|
||||
|
||||
def get_parser(self, prog_name):
|
||||
parser = super(ListPolicy, self).get_parser(prog_name)
|
||||
return parser
|
||||
|
||||
def take_action(self, parsed_args):
|
||||
client = self.app.client_manager.congressclient
|
||||
data = client.list_policy()['results']
|
||||
columns = ['id', 'owner_id']
|
||||
formatters = {'Policies': utils.format_list}
|
||||
return (columns,
|
||||
(utils.get_dict_properties(s, columns,
|
||||
formatters=formatters)
|
||||
for s in data))
|
||||
|
||||
|
||||
class GetPolicyRow(lister.Lister):
|
||||
"""Get policy row."""
|
||||
|
||||
log = logging.getLogger(__name__ + '.GetPolicyRow')
|
||||
|
||||
def get_parser(self, prog_name):
|
||||
parser = super(GetPolicyRow, self).get_parser(prog_name)
|
||||
parser.add_argument(
|
||||
'policy_name',
|
||||
metavar="<policy-name>",
|
||||
help="Name of the policy to show")
|
||||
parser.add_argument(
|
||||
'table',
|
||||
metavar="<table>",
|
||||
help="Table to get the policy row from")
|
||||
return parser
|
||||
|
||||
def take_action(self, parsed_args):
|
||||
self.log.debug('take_action(%s)' % parsed_args)
|
||||
client = self.app.client_manager.congressclient
|
||||
data = client.get_policy_rows(parsed_args.policy_name,
|
||||
parsed_args.table)['results']
|
||||
|
||||
columns = ['data']
|
||||
formatters = {'Policies': utils.format_list}
|
||||
return (columns,
|
||||
(utils.get_dict_properties(s, columns,
|
||||
formatters=formatters)
|
||||
for s in data))
|
@ -1,13 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# 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.
|
53
congressclient/tests/common.py
Normal file
53
congressclient/tests/common.py
Normal file
@ -0,0 +1,53 @@
|
||||
# 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 mock
|
||||
|
||||
from congressclient.tests import utils
|
||||
|
||||
|
||||
class TestCongressBase(utils.TestCommand):
|
||||
def setUp(self):
|
||||
super(TestCongressBase, self).setUp()
|
||||
self.app = mock.Mock(name='app')
|
||||
self.app.client_manager = mock.Mock(name='client_manager')
|
||||
self.namespace = argparse.Namespace()
|
||||
|
||||
given_show_options = [
|
||||
'-f',
|
||||
'shell',
|
||||
'-c',
|
||||
'id',
|
||||
'--prefix',
|
||||
'TST',
|
||||
]
|
||||
then_show_options = [
|
||||
('formatter', 'shell'),
|
||||
('columns', ['id']),
|
||||
('prefix', 'TST'),
|
||||
]
|
||||
given_list_options = [
|
||||
'-f',
|
||||
'csv',
|
||||
'-c',
|
||||
'id',
|
||||
'--quote',
|
||||
'all',
|
||||
]
|
||||
then_list_options = [
|
||||
('formatter', 'csv'),
|
||||
('columns', ['id']),
|
||||
('quote_mode', 'all'),
|
||||
]
|
81
congressclient/tests/fakes.py
Normal file
81
congressclient/tests/fakes.py
Normal file
@ -0,0 +1,81 @@
|
||||
# Copyright 2013 Nebula 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.
|
||||
#
|
||||
|
||||
import sys
|
||||
|
||||
import six
|
||||
|
||||
|
||||
AUTH_TOKEN = "foobar"
|
||||
AUTH_URL = "http://0.0.0.0"
|
||||
|
||||
|
||||
class FakeStdout:
|
||||
def __init__(self):
|
||||
self.content = []
|
||||
|
||||
def write(self, text):
|
||||
self.content.append(text)
|
||||
|
||||
def make_string(self):
|
||||
result = ''
|
||||
for line in self.content:
|
||||
result = result + line
|
||||
return result
|
||||
|
||||
|
||||
class FakeApp(object):
|
||||
def __init__(self, _stdout):
|
||||
self.stdout = _stdout
|
||||
self.client_manager = None
|
||||
self.stdin = sys.stdin
|
||||
self.stdout = _stdout or sys.stdout
|
||||
self.stderr = sys.stderr
|
||||
self.restapi = None
|
||||
|
||||
|
||||
class FakeClientManager(object):
|
||||
def __init__(self):
|
||||
self.compute = None
|
||||
self.identity = None
|
||||
self.image = None
|
||||
self.object = None
|
||||
self.volume = None
|
||||
self.network = None
|
||||
self.auth_ref = None
|
||||
|
||||
|
||||
class FakeModule(object):
|
||||
def __init__(self, name, version):
|
||||
self.name = name
|
||||
self.__version__ = version
|
||||
|
||||
|
||||
class FakeResource(object):
|
||||
def __init__(self, manager, info, loaded=False):
|
||||
self.manager = manager
|
||||
self._info = info
|
||||
self._add_details(info)
|
||||
self._loaded = loaded
|
||||
|
||||
def _add_details(self, info):
|
||||
for (k, v) in six.iteritems(info):
|
||||
setattr(self, k, v)
|
||||
|
||||
def __repr__(self):
|
||||
reprkeys = sorted(k for k in self.__dict__.keys() if k[0] != '_' and
|
||||
k != 'manager')
|
||||
info = ", ".join("%s=%s" % (k, getattr(self, k)) for k in reprkeys)
|
||||
return "<%s %s>" % (self.__class__.__name__, info)
|
@ -1,28 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# 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.
|
||||
|
||||
"""
|
||||
test_congressclient
|
||||
----------------------------------
|
||||
|
||||
Tests for `congressclient` module.
|
||||
"""
|
||||
|
||||
from congressclient.tests import base
|
||||
|
||||
|
||||
class TestCongressclient(base.TestCase):
|
||||
|
||||
def test_something(self):
|
||||
pass
|
93
congressclient/tests/utils.py
Normal file
93
congressclient/tests/utils.py
Normal file
@ -0,0 +1,93 @@
|
||||
# Copyright 2012-2013 OpenStack Foundation
|
||||
# Copyright 2013 Nebula 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.
|
||||
#
|
||||
|
||||
import os
|
||||
import sys
|
||||
|
||||
import fixtures
|
||||
import testtools
|
||||
|
||||
from congressclient.tests import fakes
|
||||
|
||||
|
||||
class TestCase(testtools.TestCase):
|
||||
def setUp(self):
|
||||
testtools.TestCase.setUp(self)
|
||||
|
||||
if (os.environ.get("OS_STDOUT_CAPTURE") == "True" or
|
||||
os.environ.get("OS_STDOUT_CAPTURE") == "1"):
|
||||
stdout = self.useFixture(fixtures.StringStream("stdout")).stream
|
||||
self.useFixture(fixtures.MonkeyPatch("sys.stdout", stdout))
|
||||
|
||||
if (os.environ.get("OS_STDERR_CAPTURE") == "True" or
|
||||
os.environ.get("OS_STDERR_CAPTURE") == "1"):
|
||||
stderr = self.useFixture(fixtures.StringStream("stderr")).stream
|
||||
self.useFixture(fixtures.MonkeyPatch("sys.stderr", stderr))
|
||||
|
||||
def assertNotCalled(self, m, msg=None):
|
||||
"""Assert a function was not called."""
|
||||
|
||||
if m.called:
|
||||
if not msg:
|
||||
msg = 'method %s should not have been called' % m
|
||||
self.fail(msg)
|
||||
|
||||
# 2.6 doesn't have the assert dict equals so make sure that it exists
|
||||
if tuple(sys.version_info)[0:2] < (2, 7):
|
||||
|
||||
def assertIsInstance(self, obj, cls, msg=None):
|
||||
"""self.assertTrue(isinstance(obj, cls)), with a nicer message."""
|
||||
|
||||
if not isinstance(obj, cls):
|
||||
standardMsg = '%s is not an instance of %r' % (obj, cls)
|
||||
self.fail(self._formatMessage(msg, standardMsg))
|
||||
|
||||
def assertDictEqual(self, d1, d2, msg=None):
|
||||
# Simple version taken from 2.7
|
||||
self.assertIsInstance(d1, dict,
|
||||
'First argument is not a dictionary')
|
||||
self.assertIsInstance(d2, dict,
|
||||
'Second argument is not a dictionary')
|
||||
if d1 != d2:
|
||||
if msg:
|
||||
self.fail(msg)
|
||||
else:
|
||||
standardMsg = '%r != %r' % (d1, d2)
|
||||
self.fail(standardMsg)
|
||||
|
||||
|
||||
class TestCommand(TestCase):
|
||||
"""Test cliff command classes."""
|
||||
|
||||
def setUp(self):
|
||||
super(TestCommand, self).setUp()
|
||||
# Build up a fake app
|
||||
self.fake_stdout = fakes.FakeStdout()
|
||||
self.app = fakes.FakeApp(self.fake_stdout)
|
||||
self.app.client_manager = fakes.FakeClientManager()
|
||||
|
||||
def check_parser(self, cmd, args, verify_args):
|
||||
cmd_parser = cmd.get_parser('check_parser')
|
||||
try:
|
||||
parsed_args = cmd_parser.parse_args(args)
|
||||
except SystemExit:
|
||||
raise Exception("Argument parse failed")
|
||||
for av in verify_args:
|
||||
attr, value = av
|
||||
if attr:
|
||||
self.assertIn(attr, parsed_args)
|
||||
self.assertEqual(getattr(parsed_args, attr), value)
|
||||
return parsed_args
|
0
congressclient/tests/v1/__init__.py
Normal file
0
congressclient/tests/v1/__init__.py
Normal file
147
congressclient/tests/v1/test_policy.py
Normal file
147
congressclient/tests/v1/test_policy.py
Normal file
@ -0,0 +1,147 @@
|
||||
# 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 mock
|
||||
|
||||
from congressclient.osc.v1 import policy
|
||||
from congressclient.tests import common
|
||||
|
||||
|
||||
class TestCreatePolicyRule(common.TestCongressBase):
|
||||
|
||||
def test_create_policy_rule(self):
|
||||
policy_name = 'classification'
|
||||
rule = ("port_security_group(port, security_group_name) :-"
|
||||
"neutron:ports(addr_pairs, security_groups, extra_dhcp_opts,"
|
||||
"binding_cap, status, name, admin_state_up, network_id, "
|
||||
"tenant_id, binding_vif, device_owner, mac_address, "
|
||||
"fixed_ips, port, device_id, binding_host_id1), "
|
||||
"neutron:ports.security_groups(security_groups, "
|
||||
"security_group_id), neutron:security_groups(tenant_id2, "
|
||||
"security_group_name, desc2, security_group_id)")
|
||||
|
||||
response = {"comment": "None",
|
||||
"id": "e531f2b3-3d97-42c0-b3b5-b7b6ab532018",
|
||||
"rule": rule}
|
||||
|
||||
arglist = [policy_name, rule]
|
||||
verifylist = [
|
||||
('policy_name', policy_name),
|
||||
('rule', rule),
|
||||
]
|
||||
|
||||
mocker = mock.Mock(return_value=response)
|
||||
self.app.client_manager.congressclient.create_policy_rule = mocker
|
||||
cmd = policy.CreatePolicyRule(self.app, self.namespace)
|
||||
parsed_args = self.check_parser(cmd, arglist, verifylist)
|
||||
result = list(cmd.take_action(parsed_args))
|
||||
filtered = [('comment', 'id', 'rule'),
|
||||
('None', 'e531f2b3-3d97-42c0-b3b5-b7b6ab532018',
|
||||
policy._format_rule(rule))]
|
||||
self.assertEqual(filtered, result)
|
||||
|
||||
|
||||
class TestDeletePolicyRule(common.TestCongressBase):
|
||||
def test_delete_policy_rule(self):
|
||||
policy_name = 'classification'
|
||||
rule_id = 'e531f2b3-3d97-42c0-b3b5-b7b6ab532018'
|
||||
arglist = [
|
||||
policy_name, rule_id
|
||||
]
|
||||
verifylist = [
|
||||
('policy_name', policy_name),
|
||||
('rule_id', rule_id)
|
||||
]
|
||||
mocker = mock.Mock(return_value=None)
|
||||
self.app.client_manager.congressclient.delete_policy_rule = mocker
|
||||
cmd = policy.DeletePolicyRule(self.app, self.namespace)
|
||||
|
||||
parsed_args = self.check_parser(cmd, arglist, verifylist)
|
||||
result = cmd.take_action(parsed_args)
|
||||
|
||||
mocker.assert_called_with(policy_name, rule_id)
|
||||
self.assertEqual(None, result)
|
||||
|
||||
|
||||
class TestListPolicyRules(common.TestCongressBase):
|
||||
def test_list_policy_rules(self):
|
||||
policy_name = 'classification'
|
||||
rule_id = 'e531f2b3-3d97-42c0-b3b5-b7b6ab532018'
|
||||
arglist = [
|
||||
policy_name
|
||||
]
|
||||
verifylist = [
|
||||
('policy_name', policy_name)
|
||||
]
|
||||
response = {
|
||||
"results": [{"comment": "None",
|
||||
"id": rule_id,
|
||||
"rule": "security_group(port, security_group_name)"
|
||||
}]
|
||||
}
|
||||
lister = mock.Mock(return_value=response)
|
||||
self.app.client_manager.congressclient.list_policy_rules = lister
|
||||
cmd = policy.ListPolicyRules(self.app, self.namespace)
|
||||
|
||||
parsed_args = self.check_parser(cmd, arglist, verifylist)
|
||||
result = cmd.take_action(parsed_args)
|
||||
|
||||
lister.assert_called_with(policy_name)
|
||||
self.assertEqual(['id', 'comment', 'rule'], result[0])
|
||||
|
||||
|
||||
class ListPolicy(common.TestCongressBase):
|
||||
def test_list_policy_rules(self):
|
||||
policy_name = 'classification'
|
||||
arglist = [
|
||||
]
|
||||
verifylist = [
|
||||
]
|
||||
response = {
|
||||
"results": [{"id": policy_name,
|
||||
"owner": "system"
|
||||
}]}
|
||||
lister = mock.Mock(return_value=response)
|
||||
self.app.client_manager.congressclient.list_policy = lister
|
||||
cmd = policy.ListPolicy(self.app, self.namespace)
|
||||
|
||||
parsed_args = self.check_parser(cmd, arglist, verifylist)
|
||||
result = cmd.take_action(parsed_args)
|
||||
|
||||
lister.assert_called_with()
|
||||
self.assertEqual(['id', 'owner_id'], result[0])
|
||||
|
||||
|
||||
class GetPolicyRow(common.TestCongressBase):
|
||||
|
||||
def test_list_policy_rules(self):
|
||||
policy_name = 'classification'
|
||||
table_name = 'port_security_group'
|
||||
arglist = [
|
||||
policy_name, table_name
|
||||
]
|
||||
verifylist = [
|
||||
]
|
||||
response = {"results":
|
||||
[{"data": ["69abc88b-c950-4625-801b-542e84381509",
|
||||
"default"]}]}
|
||||
|
||||
lister = mock.Mock(return_value=response)
|
||||
self.app.client_manager.congressclient.get_policy_rows = lister
|
||||
cmd = policy.GetPolicyRow(self.app, self.namespace)
|
||||
|
||||
parsed_args = self.check_parser(cmd, arglist, verifylist)
|
||||
result = cmd.take_action(parsed_args)
|
||||
|
||||
lister.assert_called_with(policy_name, table_name)
|
||||
self.assertEqual(['data'], result[0])
|
0
congressclient/v1/__init__.py
Normal file
0
congressclient/v1/__init__.py
Normal file
52
congressclient/v1/client.py
Normal file
52
congressclient/v1/client.py
Normal file
@ -0,0 +1,52 @@
|
||||
# Copyright 2014 VMWare.
|
||||
#
|
||||
# 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 keystoneclient import adapter
|
||||
|
||||
|
||||
class Client(object):
|
||||
policy_rules_path = '/policies/%s/rules'
|
||||
policy_rules_paths = '/policies/%s/rules/%s'
|
||||
policy_rows = '/policies/%s/tables/%s/rows'
|
||||
policy_rules = '/policies/%s/rules'
|
||||
policies = '/policies'
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
super(Client, self).__init__()
|
||||
|
||||
kwargs.setdefault('user_agent', 'python-congressclient')
|
||||
self.httpclient = adapter.LegacyJsonAdapter(**kwargs)
|
||||
|
||||
def create_policy_rule(self, policy_name, body=None):
|
||||
resp, body = self.httpclient.post(
|
||||
self.policy_rules_path % policy_name, body=body)
|
||||
return body
|
||||
|
||||
def delete_policy_rule(self, policy_name, rule_id):
|
||||
resp, body = self.httpclient.delete(
|
||||
self.policy_rules_paths % (policy_name, rule_id))
|
||||
return body
|
||||
|
||||
def get_policy_rows(self, policy_name, table):
|
||||
resp, body = self.httpclient.get(self.policy_rows % (policy_name,
|
||||
table))
|
||||
return body
|
||||
|
||||
def list_policy_rules(self, policy_name):
|
||||
resp, body = self.httpclient.get(self.policy_rules % (policy_name))
|
||||
return body
|
||||
|
||||
def list_policy(self):
|
||||
resp, body = self.httpclient.get(self.policies)
|
||||
return body
|
@ -1,2 +1,7 @@
|
||||
pbr>=0.6,!=0.7,<1.0
|
||||
Babel>=1.3
|
||||
cliff>=1.6.0
|
||||
oslo.i18n>=0.3.0
|
||||
python-keystoneclient>=0.10.0
|
||||
requests>=1.2.1
|
||||
six>=1.7.0
|
||||
|
13
setup.cfg
13
setup.cfg
@ -23,6 +23,17 @@ classifier =
|
||||
packages =
|
||||
congressclient
|
||||
|
||||
[entry_points]
|
||||
openstack.cli.extension =
|
||||
congressclient = congressclient.osc.osc_plugin
|
||||
|
||||
openstack.congressclient.v1 =
|
||||
congress_policy_rule_create = congressclient.osc.v1.policy:CreatePolicyRule
|
||||
congress_policy_rule_delete = congressclient.osc.v1.policy:DeletePolicyRule
|
||||
congress_policy_rules_list = congressclient.osc.v1.policy:ListPolicyRules
|
||||
congress_policy_list = congressclient.osc.v1.policy:ListPolicy
|
||||
congress_policy_row_get = congressclient.osc.v1.policy:GetPolicyRow
|
||||
|
||||
[build_sphinx]
|
||||
source-dir = doc/source
|
||||
build-dir = doc/build
|
||||
@ -43,4 +54,4 @@ input_file = congressclient/locale/python-congressclient.pot
|
||||
[extract_messages]
|
||||
keywords = _ gettext ngettext l_ lazy_gettext
|
||||
mapping_file = babel.cfg
|
||||
output_file = congressclient/locale/python-congressclient.pot
|
||||
output_file = congressclient/locale/python-congressclient.pot
|
||||
|
8
tenant-list.log
Normal file
8
tenant-list.log
Normal file
@ -0,0 +1,8 @@
|
||||
+----------------------------------+--------------------+---------+
|
||||
| id | name | enabled |
|
||||
+----------------------------------+--------------------+---------+
|
||||
| 8918ef508b3a48a1ad963cec1d7bec18 | admin | True |
|
||||
| e41ef6f35dab448699a5ca69eff60692 | demo | True |
|
||||
| c01bea38e55d44d3a87bb018b050338c | invisible_to_admin | True |
|
||||
| 3a1208bc143a4ed589288057ea8e0735 | service | True |
|
||||
+----------------------------------+--------------------+---------+
|
@ -1,4 +1,4 @@
|
||||
hacking>=0.5.6,<0.8
|
||||
hacking>=0.9.2,<0.10
|
||||
|
||||
coverage>=3.6
|
||||
discover
|
||||
@ -9,3 +9,5 @@ oslosphinx>=2.2.0.0a2
|
||||
testrepository>=0.0.18
|
||||
testscenarios>=0.4
|
||||
testtools>=0.9.34
|
||||
mock>=1.0
|
||||
WebOb>=1.2.3
|
||||
|
Loading…
Reference in New Issue
Block a user