Initialize plugin for OSC

Initial Octavia client command to list load balancers in a given
project.

Sets up many of the base classes and base files needed for
the rest of the client when it comes up.

Change-Id: I5d426e1a3a364abbe77edea5e8aaad2c8c2213c1
This commit is contained in:
Ankur Gupta 2017-03-15 18:25:49 -05:00
parent 6c069463b7
commit 970a232dc0
29 changed files with 844 additions and 57 deletions

View File

@ -2,6 +2,7 @@
test_command=OS_STDOUT_CAPTURE=${OS_STDOUT_CAPTURE:-1} \
OS_STDERR_CAPTURE=${OS_STDERR_CAPTURE:-1} \
OS_TEST_TIMEOUT=${OS_TEST_TIMEOUT:-60} \
${PYTHON:-python} -m subunit.run discover -t ./ . $LISTOPT $IDOPTION
${PYTHON:-python} -m subunit.run discover -t ./ ${OS_TEST_PATH:-./octaviaclient/tests/unit} $LISTOPT $IDOPTION
test_id_option=--load-list $IDFILE
test_list_option=--list

View File

@ -20,7 +20,7 @@ lbaas-listener-show,,LBaaS v2 Show information of a given listener.
lbaas-listener-update,,LBaaS v2 Update a given listener.
lbaas-loadbalancer-create,,LBaaS v2 Create a loadbalancer.
lbaas-loadbalancer-delete,,LBaaS v2 Delete a given loadbalancer.
lbaas-loadbalancer-list,,LBaaS v2 List loadbalancers that belong to a given tenant.
lbaas-loadbalancer-list,loadbalancer list,LBaaS v2 List loadbalancers that belong to a given tenant.
lbaas-loadbalancer-list-on-agent,,List the loadbalancers on a loadbalancer v2 agent.
lbaas-loadbalancer-show,,LBaaS v2 Show information of a given loadbalancer.
lbaas-loadbalancer-stats,,Retrieve stats for a given loadbalancer.

1 lbaas-healthmonitor-create LBaaS v2 Create a healthmonitor.
20 lbaas-listener-update LBaaS v2 Update a given listener.
21 lbaas-loadbalancer-create LBaaS v2 Create a loadbalancer.
22 lbaas-loadbalancer-delete LBaaS v2 Delete a given loadbalancer.
23 lbaas-loadbalancer-list loadbalancer list LBaaS v2 List loadbalancers that belong to a given tenant.
24 lbaas-loadbalancer-list-on-agent List the loadbalancers on a loadbalancer v2 agent.
25 lbaas-loadbalancer-show LBaaS v2 Show information of a given loadbalancer.
26 lbaas-loadbalancer-stats Retrieve stats for a given loadbalancer.

View File

@ -17,7 +17,6 @@ Getting Started
readme
installation
usage/osc_cli_plugins
contributing
Usage

View File

@ -0,0 +1,13 @@
============
loadbalancer
============
loadbalancer list
-----------------
List load balancers
.. program:: loadbalancer list
.. code:: bash
openstack loadbalancer list

View File

View File

@ -0,0 +1,40 @@
# 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.
#
"""Load Balancer v2 API Library"""
from osc_lib.api import api
class APIv2(api.BaseAPI):
"""Load Balancer v2 API"""
_endpoint_suffix = '/v2.0/lbaas'
def __init__(self, endpoint=None, **kwargs):
super(APIv2, self).__init__(endpoint=endpoint, **kwargs)
self.endpoint = self.endpoint.rstrip('/')
self._build_url()
def _build_url(self):
if not self.endpoint.endswith(self._endpoint_suffix):
self.endpoint = self.endpoint + self._endpoint_suffix
def load_balancer_list(
self,
**filter
):
url = '/loadbalancers'
load_balancer_list = self.list(url, **filter)['loadbalancers']
return load_balancer_list

View File

View File

@ -0,0 +1,66 @@
# 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.
"""OpenStackClient plugin for Load Balancer service."""
import logging
from octaviaclient.api import load_balancer_v2
from osc_lib import utils
LOG = logging.getLogger(__name__)
DEFAULT_LOADBALANCER_API_VERSION = '2.0'
API_VERSION_OPTION = 'os_loadbalancer_api_version'
API_NAME = 'load_balancer'
LOAD_BALANCER_API_TYPE = 'loadbalancer'
LOAD_BALANCER_API_VERSIONS = {
'2.0': 'octaviaclient.api.load_balancer_v2.APIv2',
}
def make_client(instance):
"""Returns a load balancer service client"""
endpoint = instance.get_endpoint_for_service_type(
'load-balancer',
region_name=instance.region_name,
interface=instance.interface,
)
client = load_balancer_v2.APIv2(
session=instance.session,
service_type='load-balancer',
endpoint=endpoint,
)
return client
def build_option_parser(parser):
"""Hook to add global options
Called from openstackclient.shell.OpenStackShell.__init__()
after the builtin parser has been initialized. This is
where a plugin can add global options such as an API version.
:param argparse.ArgumentParser parser: The parser object that
has been initialized by OpenStackShell.
"""
parser.add_argument(
'--os-loadbalancer-api-version',
metavar='<loadbalancer-api-version>',
default=utils.env(
'OS_LOADBALANCER_API_VERSION',
default=DEFAULT_LOADBALANCER_API_VERSION),
help='OSC Plugin API version, default=' +
DEFAULT_LOADBALANCER_API_VERSION +
' (Env: OS_LOADBALANCER_API_VERSION)')
return parser

View File

View File

@ -0,0 +1,41 @@
# 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.
#
"""Load Balancer action implementation"""
from cliff import lister
from osc_lib import utils
class ListLoadBalancer(lister.Lister):
"""List load balancers"""
def parsed_args(self, prog_name):
parser = super(ListLoadBalancer, self).get_parser(prog_name)
return parser
def take_action(self, parsed_args):
columns = (
'ID',
'Name',
'Project ID',
'VIP Address',
'Provisioning Status',)
data = self.app.client_manager.load_balancer.load_balancer_list()
return (columns,
(utils.get_dict_properties(
s, columns,
formatters={},
) for s in data))

View File

@ -1,23 +0,0 @@
# -*- coding: utf-8 -*-
# Copyright 2010-2011 OpenStack Foundation
# Copyright (c) 2013 Hewlett-Packard Development Company, L.P.
#
# 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 oslotest import base
class TestCase(base.BaseTestCase):
"""Test case base class for all unit tests."""

View File

@ -0,0 +1,254 @@
# 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 json
import mock
import sys
from keystoneauth1 import fixture
import requests
import six
AUTH_TOKEN = "foobar"
AUTH_URL = "http://0.0.0.0"
USERNAME = "itchy"
PASSWORD = "scratchy"
PROJECT_NAME = "poochie"
REGION_NAME = "richie"
INTERFACE = "catchy"
VERSION = "3"
TEST_RESPONSE_DICT = fixture.V2Token(token_id=AUTH_TOKEN,
user_name=USERNAME)
_s = TEST_RESPONSE_DICT.add_service('identity', name='keystone')
_s.add_endpoint(AUTH_URL + ':5000/v2.0')
_s = TEST_RESPONSE_DICT.add_service('network', name='neutron')
_s.add_endpoint(AUTH_URL + ':9696')
_s = TEST_RESPONSE_DICT.add_service('compute', name='nova')
_s.add_endpoint(AUTH_URL + ':8774/v2.1')
_s = TEST_RESPONSE_DICT.add_service('image', name='glance')
_s.add_endpoint(AUTH_URL + ':9292')
_s = TEST_RESPONSE_DICT.add_service('object', name='swift')
_s.add_endpoint(AUTH_URL + ':8080/v1')
TEST_RESPONSE_DICT_V3 = fixture.V3Token(user_name=USERNAME)
TEST_RESPONSE_DICT_V3.set_project_scope()
TEST_VERSIONS = fixture.DiscoveryList(href=AUTH_URL)
def to_unicode_dict(catalog_dict):
"""Converts dict to unicode dict
"""
if isinstance(catalog_dict, dict):
return {to_unicode_dict(key): to_unicode_dict(value)
for key, value in catalog_dict.items()}
elif isinstance(catalog_dict, list):
return [to_unicode_dict(element) for element in catalog_dict]
elif isinstance(catalog_dict, str):
return catalog_dict + u""
else:
return catalog_dict
class FakeStdout(object):
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 FakeLog(object):
def __init__(self):
self.messages = {}
def debug(self, msg):
self.messages['debug'] = msg
def info(self, msg):
self.messages['info'] = msg
def warning(self, msg):
self.messages['warning'] = msg
def error(self, msg):
self.messages['error'] = msg
def critical(self, msg):
self.messages['critical'] = msg
class FakeApp(object):
def __init__(self, _stdout, _log):
self.stdout = _stdout
self.client_manager = None
self.stdin = sys.stdin
self.stdout = _stdout or sys.stdout
self.stderr = sys.stderr
self.log = _log
class FakeOptions(object):
def __init__(self, **kwargs):
self.os_beta_command = False
class FakeClient(object):
def __init__(self, **kwargs):
self.endpoint = kwargs['endpoint']
self.token = kwargs['token']
class FakeClientManager(object):
_api_version = {
'image': '2',
}
def __init__(self):
self.compute = None
self.identity = None
self.image = None
self.object_store = None
self.volume = None
self.network = None
self.session = None
self.auth_ref = None
self.auth_plugin_name = None
self.network_endpoint_enabled = True
def get_configuration(self):
return {
'auth': {
'username': USERNAME,
'password': PASSWORD,
'token': AUTH_TOKEN,
},
'region': REGION_NAME,
'identity_api_version': VERSION,
}
def is_network_endpoint_enabled(self):
return self.network_endpoint_enabled
class FakeModule(object):
def __init__(self, name, version):
self.name = name
self.__version__ = version
# Workaround for openstacksdk case
self.version = mock.Mock()
self.version.__version__ = version
class FakeResource(object):
def __init__(self, manager=None, info=None, loaded=False, methods=None):
"""Set attributes and methods for a resource.
:param manager:
The resource manager
:param Dictionary info:
A dictionary with all attributes
:param bool loaded:
True if the resource is loaded in memory
:param Dictionary methods:
A dictionary with all methods
"""
info = info or {}
methods = methods or {}
self.__name__ = type(self).__name__
self.manager = manager
self._info = info
self._add_details(info)
self._add_methods(methods)
self._loaded = loaded
def _add_details(self, info):
for (k, v) in six.iteritems(info):
setattr(self, k, v)
def _add_methods(self, methods):
"""Fake methods with MagicMock objects.
For each <@key, @value> pairs in methods, add an callable MagicMock
object named @key as an attribute, and set the mock's return_value to
@value. When users access the attribute with (), @value will be
returned, which looks like a function call.
"""
for (name, ret) in six.iteritems(methods):
method = mock.Mock(return_value=ret)
setattr(self, name, method)
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)
def keys(self):
return self._info.keys()
def to_dict(self):
return self._info
@property
def info(self):
return self._info
def __getitem__(self, item):
return self._info.get(item)
def get(self, item, default=None):
return self._info.get(item, default)
class FakeResponse(requests.Response):
def __init__(self, headers=None, status_code=200,
data=None, encoding=None):
super(FakeResponse, self).__init__()
headers = headers or {}
self.status_code = status_code
self.headers.update(headers)
self._content = json.dumps(data)
if not isinstance(self._content, six.binary_type):
self._content = self._content.encode()
class FakeModel(dict):
def __getattr__(self, key):
try:
return self[key]
except KeyError:
raise AttributeError(key)

View File

@ -0,0 +1,123 @@
# 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 re
import shlex
import subprocess
import testtools
from tempest.lib.cli import output_parser
from tempest.lib import exceptions
COMMON_DIR = os.path.dirname(os.path.abspath(__file__))
FUNCTIONAL_DIR = os.path.normpath(os.path.join(COMMON_DIR, '..'))
ROOT_DIR = os.path.normpath(os.path.join(FUNCTIONAL_DIR, '..'))
EXAMPLE_DIR = os.path.join(ROOT_DIR, 'examples')
def execute(cmd, fail_ok=False, merge_stderr=False):
"""Executes specified command for the given action."""
cmdlist = shlex.split(cmd)
result = ''
result_err = ''
stdout = subprocess.PIPE
stderr = subprocess.STDOUT if merge_stderr else subprocess.PIPE
proc = subprocess.Popen(cmdlist, stdout=stdout, stderr=stderr)
result, result_err = proc.communicate()
result = result.decode('utf-8')
if not fail_ok and proc.returncode != 0:
raise exceptions.CommandFailed(proc.returncode, cmd, result,
result_err)
return result
class TestCase(testtools.TestCase):
delimiter_line = re.compile('^\+\-[\+\-]+\-\+$')
@classmethod
def openstack(cls, cmd, fail_ok=False):
"""Executes openstackclient command for the given action."""
return execute('openstack ' + cmd, fail_ok=fail_ok)
@classmethod
def get_openstack_configuration_value(cls, configuration):
opts = cls.get_opts([configuration])
return cls.openstack('configuration show ' + opts)
@classmethod
def get_openstack_extention_names(cls):
opts = cls.get_opts(['Name'])
return cls.openstack('extension list ' + opts)
@classmethod
def get_opts(cls, fields, output_format='value'):
return ' -f {0} {1}'.format(output_format,
' '.join(['-c ' + it for it in fields]))
@classmethod
def assertOutput(cls, expected, actual):
if expected != actual:
raise Exception(expected + ' != ' + actual)
@classmethod
def assertInOutput(cls, expected, actual):
if expected not in actual:
raise Exception(expected + ' not in ' + actual)
@classmethod
def assertsOutputNotNone(cls, observed):
if observed is None:
raise Exception('No output observed')
def assert_table_structure(self, items, field_names):
"""Verify that all items have keys listed in field_names."""
for item in items:
for field in field_names:
self.assertIn(field, item)
def assert_show_fields(self, show_output, field_names):
"""Verify that all items have keys listed in field_names."""
# field_names = ['name', 'description']
# show_output = [{'name': 'fc2b98d8faed4126b9e371eda045ade2'},
# {'description': 'description-821397086'}]
# this next line creates a flattened list of all 'keys' (like 'name',
# and 'description' out of the output
all_headers = [item for sublist in show_output for item in sublist]
for field_name in field_names:
self.assertIn(field_name, all_headers)
def parse_show_as_object(self, raw_output):
"""Return a dict with values parsed from cli output."""
items = self.parse_show(raw_output)
o = {}
for item in items:
o.update(item)
return o
def parse_show(self, raw_output):
"""Return list of dicts with item values parsed from cli output."""
items = []
table_ = output_parser.table(raw_output)
for row in table_['values']:
item = {}
item[row[0]] = row[1]
items.append(item)
return items
def parse_listing(self, raw_output):
"""Return list of dicts with basic item parsed from cli output."""
return output_parser.listing(raw_output)

View File

@ -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_octaviaclient
----------------------------------
Tests for `octaviaclient` module.
"""
from octaviaclient.tests import base
class TestOctaviaclient(base.TestCase):
def test_something(self):
pass

View File

View File

View 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.
#
"""Load Balancer v2 API Library Tests"""
from keystoneauth1 import session
from requests_mock.contrib import fixture
from octaviaclient.api import load_balancer_v2 as lb
from osc_lib.tests import utils
FAKE_ACCOUNT = 'q12we34r'
FAKE_AUTH = '11223344556677889900'
FAKE_URL = 'http://example.com/v2.0/lbaas/'
FAKE_LB = 'rainbarrel'
LIST_LB_RESP = [
{'name': 'lb1'},
{'name': 'lb2'},
]
class TestLoadBalancerv2(utils.TestCase):
def setUp(self):
super(TestLoadBalancerv2, self).setUp()
sess = session.Session()
self.api = lb.APIv2(session=sess, endpoint=FAKE_URL)
self.requests_mock = self.useFixture(fixture.Fixture())
class TestLoadBalancer(TestLoadBalancerv2):
def test_list_load_balancer_no_options(self):
self.requests_mock.register_uri(
'GET',
FAKE_URL + 'loadbalancers',
json={'loadbalancers': LIST_LB_RESP},
status_code=200,
)
ret = self.api.load_balancer_list()
self.assertEqual(LIST_LB_RESP, ret)

View File

View File

@ -0,0 +1,79 @@
# 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 copy
import mock
import uuid
from octaviaclient.tests import fakes
from osc_lib.tests import utils
LOADBALANCER = {
'id': 'lbid',
'name': 'lb1',
'project_id': 'dummyproject',
'vip_address': '192.0.2.2',
'provisioning_status': 'ONLINE',
}
class FakeLoadBalancerv2Client(object):
def __init__(self, **kwargs):
self.load_balancers = mock.Mock()
self.load_balancers.resource_class = fakes.FakeResource(None, {})
self.auth_token = kwargs['token']
self.management_url = kwargs['endpoint']
class TestLoadBalancerv2(utils.TestCommand):
def setUp(self):
super(TestLoadBalancerv2, self).setUp()
self.app.client_manager.load_balancer = FakeLoadBalancerv2Client(
endpoint=fakes.AUTH_URL,
token=fakes.AUTH_TOKEN,
)
class FakeLoadBalancer(object):
"""Fake one or more load balancers."""
@staticmethod
def create_one_load_balancer(attrs=None):
"""Create one load balancer.
:param Dictionary attrs:
A dictionary with all load balancer attributes
:return:
A FakeResource object
"""
attrs = attrs or {}
# Set default attribute
lb_info = {
'id': str(uuid.uuid4()),
'name': 'lb-name-' + uuid.uuid4().hex,
'project_id': uuid.uuid4().hex,
'vip_address': '192.0.2.2',
'provisioning_status': 'ONLINE',
}
lb_info.update(attrs)
lb = fakes.FakeResource(
info=copy.deepcopy(lb_info),
loaded=True)
return lb

View File

@ -0,0 +1,81 @@
# 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 copy
import mock
from octaviaclient.osc.v2 import load_balancer as load_balancer
from octaviaclient.tests.unit.osc.v2 import fakes as lb_fakes
AUTH_TOKEN = "foobar"
AUTH_URL = "http://192.0.2.2"
class TestLoadBalancer(lb_fakes.TestLoadBalancerv2):
def setUp(self):
super(TestLoadBalancer, self).setUp()
self.lb_mock = self.app.client_manager.load_balancer.load_balancers
self.lb_mock.reset_mock()
class TestLoadBalancerList(TestLoadBalancer):
_lb = lb_fakes.FakeLoadBalancer.create_one_load_balancer()
columns = (
'ID',
'Name',
'Project ID',
'VIP Address',
'Provisioning Status',
)
datalist = (
(
_lb.id,
_lb.name,
_lb.project_id,
_lb.vip_address,
_lb.provisioning_status,
),
)
info = {
'id': _lb.id,
'name': _lb.name,
'project_id': _lb.project_id,
'vip_address': _lb.vip_address,
'provisioning_status': _lb.provisioning_status,
}
lb_info = copy.deepcopy(info)
def setUp(self):
super(TestLoadBalancerList, self).setUp()
self.api_mock = mock.Mock()
self.api_mock.load_balancer_list.return_value = [self.lb_info]
lb_client = self.app.client_manager
lb_client.load_balancer = self.api_mock
self.cmd = load_balancer.ListLoadBalancer(self.app, None)
def test_load_balancer_list_no_options(self):
arglist = []
verifylist = []
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
columns, data = self.cmd.take_action(parsed_args)
self.api_mock.load_balancer_list.assert_called_with()
self.assertEqual(self.columns, columns)
self.assertEqual(self.datalist, tuple(data))

View File

@ -0,0 +1,75 @@
# 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 fixtures
import os
import testtools
from octaviaclient.tests import fakes
class ParserException(Exception):
pass
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)
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.fake_log = fakes.FakeLog()
self.app = fakes.FakeApp(self.fake_stdout, self.fake_log)
self.app.client_manager = fakes.FakeClientManager()
self.app.options = fakes.FakeOptions()
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 ParserException("Argument parse failed")
for av in verify_args:
attr, value = av
if attr:
self.assertIn(attr, parsed_args)
self.assertEqual(value, getattr(parsed_args, attr))
return parsed_args

View File

@ -0,0 +1,4 @@
---
features:
- |
Add initial load balancer command ``loadbalancer list``.

View File

@ -24,7 +24,10 @@ packages =
[entry_points]
openstack.cli.extension =
load-balancer = octaviaclient.osc.plugin
load_balancer = octaviaclient.osc.plugin
openstack.load_balancer.v2 =
loadbalancer_list = octaviaclient.osc.v2.load_balancer:ListLoadBalancer
[build_sphinx]
source-dir = doc/source

View File

@ -3,9 +3,12 @@
# process, which may cause wedges in the gate later.
hacking>=0.12.0,!=0.13.0,<0.14 # Apache-2.0
requests!=2.12.2,!=2.13.0,>=2.10.0 # Apache-2.0
requests-mock>=1.1 # Apache-2.0
coverage>=4.0 # Apache-2.0
mock>=2.0 # BSD
python-subunit>=0.0.18 # Apache-2.0/BSD
python-openstackclient>=3.3.0 # Apache-2.0
sphinx>=1.5.1 # BSD
oslosphinx>=4.7.0 # Apache-2.0
oslotest>=1.10.0 # Apache-2.0

View File

@ -29,7 +29,10 @@ commands =
sphinx-build -a -E -W -d releasenotes/build/doctrees -b html releasenotes/source releasenotes/build/html
[testenv:debug]
commands = oslo_debug_helper {posargs}
passenv = OS_*
commands =
pip install -q -U ipdb
oslo_debug_helper -t octaviaclient/tests {posargs}
[flake8]
# E123, E125 skipped as they are invalid PEP-8.