Remove quantumclient and proxy to neutronclient

Change-Id: I95a351071e68dfc2d67f3895c87f45ad2221767f
This commit is contained in:
Mark McClain
2013-09-25 19:13:38 -04:00
parent 8ed38707b1
commit cbb83121c0
66 changed files with 35 additions and 10992 deletions

View File

@@ -1 +1,2 @@
This is the client API library for Quantum.
This package provides a compatibility layer for code that was built to require
the old Quantum API Client. New code should use the neutronclient module.

View File

@@ -61,3 +61,8 @@ Release Notes
* made the publicURL the default endpoint instead of adminURL
* add ability to update security group name (requires 2013.2-Havana or later)
* add flake8 and pbr support for testing and building
2.2.4
-----
* add compatibility layer to proxy neutronclient
* removes all quantumclient code

View File

@@ -1,7 +0,0 @@
[DEFAULT]
# The list of modules to copy from openstack-common
modules=exception,gettextutils,jsonutils,strutils,timeutils
# The base module to hold the copy of openstack.common
base=quantumclient

View File

@@ -1,128 +0,0 @@
#!/bin/bash
set -x
function die() {
local exitcode=$?
set +o xtrace
echo $@
exit $exitcode
}
noauth_tenant_id=me
if [ $1 == 'noauth' ]; then
NOAUTH="--tenant_id $noauth_tenant_id"
else
NOAUTH=
fi
FORMAT=" --request-format xml"
# test the CRUD of network
network=mynet1
quantum net-create $FORMAT $NOAUTH $network || die "fail to create network $network"
temp=`quantum net-list $FORMAT -- --name $network --fields id | wc -l`
echo $temp
if [ $temp -ne 5 ]; then
die "networks with name $network is not unique or found"
fi
network_id=`quantum net-list -- --name $network --fields id | tail -n 2 | head -n 1 | cut -d' ' -f 2`
echo "ID of network with name $network is $network_id"
quantum net-show $FORMAT $network || die "fail to show network $network"
quantum net-show $FORMAT $network_id || die "fail to show network $network_id"
quantum net-update $FORMAT $network --admin_state_up False || die "fail to update network $network"
quantum net-update $FORMAT $network_id --admin_state_up True || die "fail to update network $network_id"
quantum net-list $FORMAT -c id -- --id fakeid || die "fail to list networks with column selection on empty list"
# test the CRUD of subnet
subnet=mysubnet1
cidr=10.0.1.3/24
quantum subnet-create $FORMAT $NOAUTH $network $cidr --name $subnet || die "fail to create subnet $subnet"
tempsubnet=`quantum subnet-list $FORMAT -- --name $subnet --fields id | wc -l`
echo $tempsubnet
if [ $tempsubnet -ne 5 ]; then
die "subnets with name $subnet is not unique or found"
fi
subnet_id=`quantum subnet-list $FORMAT -- --name $subnet --fields id | tail -n 2 | head -n 1 | cut -d' ' -f 2`
echo "ID of subnet with name $subnet is $subnet_id"
quantum subnet-show $FORMAT $subnet || die "fail to show subnet $subnet"
quantum subnet-show $FORMAT $subnet_id || die "fail to show subnet $subnet_id"
quantum subnet-update $FORMAT $subnet --dns_namesevers host1 || die "fail to update subnet $subnet"
quantum subnet-update $FORMAT $subnet_id --dns_namesevers host2 || die "fail to update subnet $subnet_id"
# test the crud of ports
port=myport1
quantum port-create $FORMAT $NOAUTH $network --name $port || die "fail to create port $port"
tempport=`quantum port-list $FORMAT -- --name $port --fields id | wc -l`
echo $tempport
if [ $tempport -ne 5 ]; then
die "ports with name $port is not unique or found"
fi
port_id=`quantum port-list $FORMAT -- --name $port --fields id | tail -n 2 | head -n 1 | cut -d' ' -f 2`
echo "ID of port with name $port is $port_id"
quantum port-show $FORMAT $port || die "fail to show port $port"
quantum port-show $FORMAT $port_id || die "fail to show port $port_id"
quantum port-update $FORMAT $port --device_id deviceid1 || die "fail to update port $port"
quantum port-update $FORMAT $port_id --device_id deviceid2 || die "fail to update port $port_id"
# test quota commands RUD
DEFAULT_NETWORKS=10
DEFAULT_PORTS=50
tenant_id=tenant_a
tenant_id_b=tenant_b
quantum quota-update $FORMAT --tenant_id $tenant_id --network 30 || die "fail to update quota for tenant $tenant_id"
quantum quota-update $FORMAT --tenant_id $tenant_id_b --network 20 || die "fail to update quota for tenant $tenant_id"
networks=`quantum quota-list $FORMAT -c network -c tenant_id | grep $tenant_id | awk '{print $2}'`
if [ $networks -ne 30 ]; then
die "networks quota should be 30"
fi
networks=`quantum quota-list $FORMAT -c network -c tenant_id | grep $tenant_id_b | awk '{print $2}'`
if [ $networks -ne 20 ]; then
die "networks quota should be 20"
fi
networks=`quantum quota-show $FORMAT --tenant_id $tenant_id | grep network | awk -F'|' '{print $3}'`
if [ $networks -ne 30 ]; then
die "networks quota should be 30"
fi
quantum quota-delete $FORMAT --tenant_id $tenant_id || die "fail to delete quota for tenant $tenant_id"
networks=`quantum quota-show $FORMAT --tenant_id $tenant_id | grep network | awk -F'|' '{print $3}'`
if [ $networks -ne $DEFAULT_NETWORKS ]; then
die "networks quota should be $DEFAULT_NETWORKS"
fi
# update self
if [ "t$NOAUTH" = "t" ]; then
# with auth
quantum quota-update $FORMAT --port 99 || die "fail to update quota for self"
ports=`quantum quota-show $FORMAT | grep port | awk -F'|' '{print $3}'`
if [ $ports -ne 99 ]; then
die "ports quota should be 99"
fi
ports=`quantum quota-list $FORMAT -c port | grep 99 | awk '{print $2}'`
if [ $ports -ne 99 ]; then
die "ports quota should be 99"
fi
quantum quota-delete $FORMAT || die "fail to delete quota for tenant self"
ports=`quantum quota-show $FORMAT | grep port | awk -F'|' '{print $3}'`
if [ $ports -ne $DEFAULT_PORTS ]; then
die "ports quota should be $DEFAULT_PORTS"
fi
else
# without auth
quantum quota-update $FORMAT --port 100
if [ $? -eq 0 ]; then
die "without valid context on server, quota update command should fail."
fi
quantum quota-show $FORMAT
if [ $? -eq 0 ]; then
die "without valid context on server, quota show command should fail."
fi
quantum quota-delete $FORMAT
if [ $? -eq 0 ]; then
die "without valid context on server, quota delete command should fail."
fi
quantum quota-list $FORMAT || die "fail to update quota for self"
fi

View File

@@ -1,249 +1,3 @@
# Copyright 2012 OpenStack LLC.
# 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.
#
# vim: tabstop=4 shiftwidth=4 softtabstop=4
from neutronclient import client
try:
import json
except ImportError:
import simplejson as json
import logging
import os
import urlparse
# Python 2.5 compat fix
if not hasattr(urlparse, 'parse_qsl'):
import cgi
urlparse.parse_qsl = cgi.parse_qsl
import httplib2
from quantumclient.common import exceptions
from quantumclient.common import utils
_logger = logging.getLogger(__name__)
if 'QUANTUMCLIENT_DEBUG' in os.environ and os.environ['QUANTUMCLIENT_DEBUG']:
ch = logging.StreamHandler()
_logger.setLevel(logging.DEBUG)
_logger.addHandler(ch)
class ServiceCatalog(object):
"""Helper methods for dealing with a Keystone Service Catalog."""
def __init__(self, resource_dict):
self.catalog = resource_dict
def get_token(self):
"""Fetch token details fron service catalog."""
token = {'id': self.catalog['access']['token']['id'],
'expires': self.catalog['access']['token']['expires'], }
try:
token['user_id'] = self.catalog['access']['user']['id']
token['tenant_id'] = (
self.catalog['access']['token']['tenant']['id'])
except Exception:
# just leave the tenant and user out if it doesn't exist
pass
return token
def url_for(self, attr=None, filter_value=None,
service_type='network', endpoint_type='publicURL'):
"""Fetch the URL from the Quantum service for
a particular endpoint type. If none given, return
publicURL.
"""
catalog = self.catalog['access'].get('serviceCatalog', [])
matching_endpoints = []
for service in catalog:
if service['type'] != service_type:
continue
endpoints = service['endpoints']
for endpoint in endpoints:
if not filter_value or endpoint.get(attr) == filter_value:
matching_endpoints.append(endpoint)
if not matching_endpoints:
raise exceptions.EndpointNotFound()
elif len(matching_endpoints) > 1:
raise exceptions.AmbiguousEndpoints(message=matching_endpoints)
else:
if endpoint_type not in matching_endpoints[0]:
raise exceptions.EndpointTypeNotFound(message=endpoint_type)
return matching_endpoints[0][endpoint_type]
class HTTPClient(httplib2.Http):
"""Handles the REST calls and responses, include authn."""
USER_AGENT = 'python-quantumclient'
def __init__(self, username=None, tenant_name=None,
password=None, auth_url=None,
token=None, region_name=None, timeout=None,
endpoint_url=None, insecure=False,
endpoint_type='publicURL',
auth_strategy='keystone', **kwargs):
super(HTTPClient, self).__init__(timeout=timeout)
self.username = username
self.tenant_name = tenant_name
self.password = password
self.auth_url = auth_url.rstrip('/') if auth_url else None
self.endpoint_type = endpoint_type
self.region_name = region_name
self.auth_token = token
self.content_type = 'application/json'
self.endpoint_url = endpoint_url
self.auth_strategy = auth_strategy
# httplib2 overrides
self.force_exception_to_status_code = True
self.disable_ssl_certificate_validation = insecure
def _cs_request(self, *args, **kwargs):
kargs = {}
kargs.setdefault('headers', kwargs.get('headers', {}))
kargs['headers']['User-Agent'] = self.USER_AGENT
if 'content_type' in kwargs:
kargs['headers']['Content-Type'] = kwargs['content_type']
kargs['headers']['Accept'] = kwargs['content_type']
else:
kargs['headers']['Content-Type'] = self.content_type
kargs['headers']['Accept'] = self.content_type
if 'body' in kwargs:
kargs['body'] = kwargs['body']
args = utils.safe_encode_list(args)
kargs = utils.safe_encode_dict(kargs)
utils.http_log_req(_logger, args, kargs)
resp, body = self.request(*args, **kargs)
utils.http_log_resp(_logger, resp, body)
status_code = self.get_status_code(resp)
if status_code == 401:
raise exceptions.Unauthorized(message=body)
elif status_code == 403:
raise exceptions.Forbidden(message=body)
return resp, body
def authenticate_and_fetch_endpoint_url(self):
if not self.auth_token:
self.authenticate()
elif not self.endpoint_url:
self.endpoint_url = self._get_endpoint_url()
def do_request(self, url, method, **kwargs):
self.authenticate_and_fetch_endpoint_url()
# Perform the request once. If we get a 401 back then it
# might be because the auth token expired, so try to
# re-authenticate and try again. If it still fails, bail.
try:
kwargs.setdefault('headers', {})
kwargs['headers']['X-Auth-Token'] = self.auth_token
resp, body = self._cs_request(self.endpoint_url + url, method,
**kwargs)
return resp, body
except exceptions.Unauthorized:
self.authenticate()
kwargs.setdefault('headers', {})
kwargs['headers']['X-Auth-Token'] = self.auth_token
resp, body = self._cs_request(
self.endpoint_url + url, method, **kwargs)
return resp, body
def _extract_service_catalog(self, body):
"""Set the client's service catalog from the response data."""
self.service_catalog = ServiceCatalog(body)
try:
sc = self.service_catalog.get_token()
self.auth_token = sc['id']
self.auth_tenant_id = sc.get('tenant_id')
self.auth_user_id = sc.get('user_id')
except KeyError:
raise exceptions.Unauthorized()
self.endpoint_url = self.service_catalog.url_for(
attr='region', filter_value=self.region_name,
endpoint_type=self.endpoint_type)
def authenticate(self):
if self.auth_strategy != 'keystone':
raise exceptions.Unauthorized(message='unknown auth strategy')
body = {'auth': {'passwordCredentials':
{'username': self.username,
'password': self.password, },
'tenantName': self.tenant_name, }, }
token_url = self.auth_url + "/tokens"
# Make sure we follow redirects when trying to reach Keystone
tmp_follow_all_redirects = self.follow_all_redirects
self.follow_all_redirects = True
try:
resp, body = self._cs_request(token_url, "POST",
body=json.dumps(body),
content_type="application/json")
finally:
self.follow_all_redirects = tmp_follow_all_redirects
status_code = self.get_status_code(resp)
if status_code != 200:
raise exceptions.Unauthorized(message=body)
if body:
try:
body = json.loads(body)
except ValueError:
pass
else:
body = None
self._extract_service_catalog(body)
def _get_endpoint_url(self):
url = self.auth_url + '/tokens/%s/endpoints' % self.auth_token
try:
resp, body = self._cs_request(url, "GET")
except exceptions.Unauthorized:
# rollback to authenticate() to handle case when quantum client
# is initialized just before the token is expired
self.authenticate()
return self.endpoint_url
body = json.loads(body)
for endpoint in body.get('endpoints', []):
if (endpoint['type'] == 'network' and
endpoint.get('region') == self.region_name):
if self.endpoint_type not in endpoint:
raise exceptions.EndpointTypeNotFound(
message=self.endpoint_type)
return endpoint[self.endpoint_type]
raise exceptions.EndpointNotFound()
def get_auth_info(self):
return {'auth_token': self.auth_token,
'auth_tenant_id': self.auth_tenant_id,
'auth_user_id': self.auth_user_id,
'endpoint_url': self.endpoint_url}
def get_status_code(self, response):
"""Returns the integer status code from the response.
Either a Webob.Response (used in testing) or httplib.Response
is returned.
"""
if hasattr(response, 'status_int'):
return response.status_int
else:
return response.status
HTTPClient = client.HTTPClient

View File

@@ -14,11 +14,3 @@
# License for the specific language governing permissions and limitations
# under the License.
# @author: Somik Behera, Nicira Networks, Inc.
import gettext
t = gettext.translation('quantumclient', fallback=True)
def _(msg):
return t.ugettext(msg)

View File

@@ -1,87 +0,0 @@
# Copyright 2012 OpenStack LLC.
# 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.
#
# vim: tabstop=4 shiftwidth=4 softtabstop=4
"""Manage access to the clients, including authenticating when needed.
"""
import logging
from quantumclient import client
from quantumclient.quantum import client as quantum_client
LOG = logging.getLogger(__name__)
class ClientCache(object):
"""Descriptor class for caching created client handles.
"""
def __init__(self, factory):
self.factory = factory
self._handle = None
def __get__(self, instance, owner):
# Tell the ClientManager to login to keystone
if self._handle is None:
self._handle = self.factory(instance)
return self._handle
class ClientManager(object):
"""Manages access to API clients, including authentication.
"""
quantum = ClientCache(quantum_client.make_client)
def __init__(self, token=None, url=None,
auth_url=None,
endpoint_type=None,
tenant_name=None, tenant_id=None,
username=None, password=None,
region_name=None,
api_version=None,
auth_strategy=None,
insecure=False
):
self._token = token
self._url = url
self._auth_url = auth_url
self._endpoint_type = endpoint_type
self._tenant_name = tenant_name
self._tenant_id = tenant_id
self._username = username
self._password = password
self._region_name = region_name
self._api_version = api_version
self._service_catalog = None
self._auth_strategy = auth_strategy
self._insecure = insecure
return
def initialize(self):
if not self._url:
httpclient = client.HTTPClient(username=self._username,
tenant_name=self._tenant_name,
password=self._password,
region_name=self._region_name,
auth_url=self._auth_url,
endpoint_type=self._endpoint_type,
insecure=self._insecure)
httpclient.authenticate()
# Populate other password flow attributes
self._token = httpclient.auth_token
self._url = httpclient.endpoint_url

View File

@@ -1,41 +0,0 @@
# Copyright 2012 OpenStack LLC.
# 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.
#
# vim: tabstop=4 shiftwidth=4 softtabstop=4
"""
OpenStack base command
"""
from cliff import command
class OpenStackCommand(command.Command):
"""Base class for OpenStack commands
"""
api = None
def run(self, parsed_args):
if not self.api:
return
else:
return super(OpenStackCommand, self).run(parsed_args)
def get_data(self, parsed_args):
pass
def take_action(self, parsed_args):
return self.get_data(parsed_args)

View File

@@ -1,43 +0,0 @@
# Copyright (c) 2012 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.
EXT_NS = '_extension_ns'
XML_NS_V20 = 'http://openstack.org/quantum/api/v2.0'
XSI_NAMESPACE = "http://www.w3.org/2001/XMLSchema-instance"
XSI_ATTR = "xsi:nil"
XSI_NIL_ATTR = "xmlns:xsi"
TYPE_XMLNS = "xmlns:quantum"
TYPE_ATTR = "quantum:type"
VIRTUAL_ROOT_KEY = "_v_root"
ATOM_NAMESPACE = "http://www.w3.org/2005/Atom"
ATOM_XMLNS = "xmlns:atom"
ATOM_LINK_NOTATION = "{%s}link" % ATOM_NAMESPACE
TYPE_BOOL = "bool"
TYPE_INT = "int"
TYPE_LONG = "long"
TYPE_FLOAT = "float"
TYPE_LIST = "list"
TYPE_DICT = "dict"
PLURALS = {'networks': 'network',
'ports': 'port',
'subnets': 'subnet',
'dns_nameservers': 'dns_nameserver',
'host_routes': 'host_route',
'allocation_pools': 'allocation_pool',
'fixed_ips': 'fixed_ip',
'extensions': 'extension'}

View File

@@ -15,155 +15,6 @@
# License for the specific language governing permissions and limitations
# under the License.
from quantumclient.common import _
from neutronclient.common.exceptions import * # noqa
"""
Quantum base exception handling.
"""
class QuantumException(Exception):
"""Base Quantum Exception
Taken from nova.exception.NovaException
To correctly use this class, inherit from it and define
a 'message' property. That message will get printf'd
with the keyword arguments provided to the constructor.
"""
message = _("An unknown exception occurred.")
def __init__(self, **kwargs):
try:
self._error_string = self.message % kwargs
except Exception:
# at least get the core message out if something happened
self._error_string = self.message
def __str__(self):
return self._error_string
class NotFound(QuantumException):
pass
class QuantumClientException(QuantumException):
def __init__(self, **kwargs):
message = kwargs.get('message')
self.status_code = kwargs.get('status_code', 0)
if message:
self.message = message
super(QuantumClientException, self).__init__(**kwargs)
# NOTE: on the client side, we use different exception types in order
# to allow client library users to handle server exceptions in try...except
# blocks. The actual error message is the one generated on the server side
class NetworkNotFoundClient(QuantumClientException):
pass
class PortNotFoundClient(QuantumClientException):
pass
class MalformedResponseBody(QuantumException):
message = _("Malformed response body: %(reason)s")
class StateInvalidClient(QuantumClientException):
pass
class NetworkInUseClient(QuantumClientException):
pass
class PortInUseClient(QuantumClientException):
pass
class AlreadyAttachedClient(QuantumClientException):
pass
class Unauthorized(QuantumClientException):
message = _("Unauthorized: bad credentials.")
class Forbidden(QuantumClientException):
message = _("Forbidden: your credentials don't give you access to this "
"resource.")
class EndpointNotFound(QuantumClientException):
"""Could not find Service or Region in Service Catalog."""
message = _("Could not find Service or Region in Service Catalog.")
class EndpointTypeNotFound(QuantumClientException):
"""Could not find endpoint type in Service Catalog."""
def __str__(self):
msg = "Could not find endpoint type %s in Service Catalog."
return msg % repr(self.message)
class AmbiguousEndpoints(QuantumClientException):
"""Found more than one matching endpoint in Service Catalog."""
def __str__(self):
return "AmbiguousEndpoints: %s" % repr(self.message)
class QuantumCLIError(QuantumClientException):
"""Exception raised when command line parsing fails."""
pass
class RequestURITooLong(QuantumClientException):
"""Raised when a request fails with HTTP error 414."""
def __init__(self, **kwargs):
self.excess = kwargs.get('excess', 0)
super(RequestURITooLong, self).__init__(**kwargs)
class ConnectionFailed(QuantumClientException):
message = _("Connection to quantum failed: %(reason)s")
class BadInputError(Exception):
"""Error resulting from a client sending bad input to a server."""
pass
class Error(Exception):
def __init__(self, message=None):
super(Error, self).__init__(message)
class MalformedRequestBody(QuantumException):
message = _("Malformed request body: %(reason)s")
class Invalid(Error):
pass
class InvalidContentType(Invalid):
message = _("Invalid content type %(content_type)s.")
class UnsupportedVersion(Exception):
"""Indicates that the user is trying to use an unsupported
version of the API
"""
pass
class CommandError(Exception):
pass
QuantumException = NeutronException

View File

@@ -1,410 +0,0 @@
# Copyright 2013 OpenStack LLC.
# 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.
#
# vim: tabstop=4 shiftwidth=4 softtabstop=4
###
### Codes from quantum wsgi
###
import logging
from xml.etree import ElementTree as etree
from xml.parsers import expat
from quantumclient.common import constants
from quantumclient.common import exceptions as exception
from quantumclient.openstack.common.gettextutils import _
from quantumclient.openstack.common import jsonutils
LOG = logging.getLogger(__name__)
class ActionDispatcher(object):
"""Maps method name to local methods through action name."""
def dispatch(self, *args, **kwargs):
"""Find and call local method."""
action = kwargs.pop('action', 'default')
action_method = getattr(self, str(action), self.default)
return action_method(*args, **kwargs)
def default(self, data):
raise NotImplementedError()
class DictSerializer(ActionDispatcher):
"""Default request body serialization."""
def serialize(self, data, action='default'):
return self.dispatch(data, action=action)
def default(self, data):
return ""
class JSONDictSerializer(DictSerializer):
"""Default JSON request body serialization."""
def default(self, data):
def sanitizer(obj):
return unicode(obj)
return jsonutils.dumps(data, default=sanitizer)
class XMLDictSerializer(DictSerializer):
def __init__(self, metadata=None, xmlns=None):
"""XMLDictSerializer constructor.
:param metadata: information needed to deserialize xml into
a dictionary.
:param xmlns: XML namespace to include with serialized xml
"""
super(XMLDictSerializer, self).__init__()
self.metadata = metadata or {}
if not xmlns:
xmlns = self.metadata.get('xmlns')
if not xmlns:
xmlns = constants.XML_NS_V20
self.xmlns = xmlns
def default(self, data):
"""Default serializer of XMLDictSerializer.
:param data: expect data to contain a single key as XML root, or
contain another '*_links' key as atom links. Other
case will use 'VIRTUAL_ROOT_KEY' as XML root.
"""
try:
links = None
has_atom = False
if data is None:
root_key = constants.VIRTUAL_ROOT_KEY
root_value = None
else:
link_keys = [k for k in data.iterkeys() or []
if k.endswith('_links')]
if link_keys:
links = data.pop(link_keys[0], None)
has_atom = True
root_key = (len(data) == 1 and
data.keys()[0] or constants.VIRTUAL_ROOT_KEY)
root_value = data.get(root_key, data)
doc = etree.Element("_temp_root")
used_prefixes = []
self._to_xml_node(doc, self.metadata, root_key,
root_value, used_prefixes)
if links:
self._create_link_nodes(list(doc)[0], links)
return self.to_xml_string(list(doc)[0], used_prefixes, has_atom)
except AttributeError as e:
LOG.exception(str(e))
return ''
def __call__(self, data):
# Provides a migration path to a cleaner WSGI layer, this
# "default" stuff and extreme extensibility isn't being used
# like originally intended
return self.default(data)
def to_xml_string(self, node, used_prefixes, has_atom=False):
self._add_xmlns(node, used_prefixes, has_atom)
return etree.tostring(node, encoding='UTF-8')
#NOTE (ameade): the has_atom should be removed after all of the
# xml serializers and view builders have been updated to the current
# spec that required all responses include the xmlns:atom, the has_atom
# flag is to prevent current tests from breaking
def _add_xmlns(self, node, used_prefixes, has_atom=False):
node.set('xmlns', self.xmlns)
node.set(constants.TYPE_XMLNS, self.xmlns)
if has_atom:
node.set(constants.ATOM_XMLNS, constants.ATOM_NAMESPACE)
node.set(constants.XSI_NIL_ATTR, constants.XSI_NAMESPACE)
ext_ns = self.metadata.get(constants.EXT_NS, {})
for prefix in used_prefixes:
if prefix in ext_ns:
node.set('xmlns:' + prefix, ext_ns[prefix])
def _to_xml_node(self, parent, metadata, nodename, data, used_prefixes):
"""Recursive method to convert data members to XML nodes."""
result = etree.SubElement(parent, nodename)
if ":" in nodename:
used_prefixes.append(nodename.split(":", 1)[0])
#TODO(bcwaldon): accomplish this without a type-check
if isinstance(data, list):
if not data:
result.set(
constants.TYPE_ATTR,
constants.TYPE_LIST)
return result
singular = metadata.get('plurals', {}).get(nodename, None)
if singular is None:
if nodename.endswith('s'):
singular = nodename[:-1]
else:
singular = 'item'
for item in data:
self._to_xml_node(result, metadata, singular, item,
used_prefixes)
#TODO(bcwaldon): accomplish this without a type-check
elif isinstance(data, dict):
if not data:
result.set(
constants.TYPE_ATTR,
constants.TYPE_DICT)
return result
attrs = metadata.get('attributes', {}).get(nodename, {})
for k, v in data.items():
if k in attrs:
result.set(k, str(v))
else:
self._to_xml_node(result, metadata, k, v,
used_prefixes)
elif data is None:
result.set(constants.XSI_ATTR, 'true')
else:
if isinstance(data, bool):
result.set(
constants.TYPE_ATTR,
constants.TYPE_BOOL)
elif isinstance(data, int):
result.set(
constants.TYPE_ATTR,
constants.TYPE_INT)
elif isinstance(data, long):
result.set(
constants.TYPE_ATTR,
constants.TYPE_LONG)
elif isinstance(data, float):
result.set(
constants.TYPE_ATTR,
constants.TYPE_FLOAT)
LOG.debug(_("Data %(data)s type is %(type)s"),
{'data': data,
'type': type(data)})
if isinstance(data, str):
result.text = unicode(data, 'utf-8')
else:
result.text = unicode(data)
return result
def _create_link_nodes(self, xml_doc, links):
for link in links:
link_node = etree.SubElement(xml_doc, 'atom:link')
link_node.set('rel', link['rel'])
link_node.set('href', link['href'])
class TextDeserializer(ActionDispatcher):
"""Default request body deserialization."""
def deserialize(self, datastring, action='default'):
return self.dispatch(datastring, action=action)
def default(self, datastring):
return {}
class JSONDeserializer(TextDeserializer):
def _from_json(self, datastring):
try:
return jsonutils.loads(datastring)
except ValueError:
msg = _("Cannot understand JSON")
raise exception.MalformedRequestBody(reason=msg)
def default(self, datastring):
return {'body': self._from_json(datastring)}
class XMLDeserializer(TextDeserializer):
def __init__(self, metadata=None):
"""XMLDeserializer constructor.
:param metadata: information needed to deserialize xml into
a dictionary.
"""
super(XMLDeserializer, self).__init__()
self.metadata = metadata or {}
xmlns = self.metadata.get('xmlns')
if not xmlns:
xmlns = constants.XML_NS_V20
self.xmlns = xmlns
def _get_key(self, tag):
tags = tag.split("}", 1)
if len(tags) == 2:
ns = tags[0][1:]
bare_tag = tags[1]
ext_ns = self.metadata.get(constants.EXT_NS, {})
if ns == self.xmlns:
return bare_tag
for prefix, _ns in ext_ns.items():
if ns == _ns:
return prefix + ":" + bare_tag
else:
return tag
def _get_links(self, root_tag, node):
link_nodes = node.findall(constants.ATOM_LINK_NOTATION)
root_tag = self._get_key(node.tag)
link_key = "%s_links" % root_tag
link_list = []
for link in link_nodes:
link_list.append({'rel': link.get('rel'),
'href': link.get('href')})
# Remove link node in order to avoid link node being
# processed as an item in _from_xml_node
node.remove(link)
return link_list and {link_key: link_list} or {}
def _from_xml(self, datastring):
if datastring is None:
return None
plurals = set(self.metadata.get('plurals', {}))
try:
node = etree.fromstring(datastring)
root_tag = self._get_key(node.tag)
links = self._get_links(root_tag, node)
result = self._from_xml_node(node, plurals)
# There is no case where root_tag = constants.VIRTUAL_ROOT_KEY
# and links is not None because of the way data are serialized
if root_tag == constants.VIRTUAL_ROOT_KEY:
return result
return dict({root_tag: result}, **links)
except Exception as e:
parseError = False
# Python2.7
if (hasattr(etree, 'ParseError') and
isinstance(e, getattr(etree, 'ParseError'))):
parseError = True
# Python2.6
elif isinstance(e, expat.ExpatError):
parseError = True
if parseError:
msg = _("Cannot understand XML")
raise exception.MalformedRequestBody(reason=msg)
else:
raise
def _from_xml_node(self, node, listnames):
"""Convert a minidom node to a simple Python type.
:param listnames: list of XML node names whose subnodes should
be considered list items.
"""
attrNil = node.get(str(etree.QName(constants.XSI_NAMESPACE, "nil")))
attrType = node.get(str(etree.QName(
self.metadata.get('xmlns'), "type")))
if (attrNil and attrNil.lower() == 'true'):
return None
elif not len(node) and not node.text:
if (attrType and attrType == constants.TYPE_DICT):
return {}
elif (attrType and attrType == constants.TYPE_LIST):
return []
else:
return ''
elif (len(node) == 0 and node.text):
converters = {constants.TYPE_BOOL:
lambda x: x.lower() == 'true',
constants.TYPE_INT:
lambda x: int(x),
constants.TYPE_LONG:
lambda x: long(x),
constants.TYPE_FLOAT:
lambda x: float(x)}
if attrType and attrType in converters:
return converters[attrType](node.text)
else:
return node.text
elif self._get_key(node.tag) in listnames:
return [self._from_xml_node(n, listnames) for n in node]
else:
result = dict()
for attr in node.keys():
if (attr == 'xmlns' or
attr.startswith('xmlns:') or
attr == constants.XSI_ATTR or
attr == constants.TYPE_ATTR):
continue
result[self._get_key(attr)] = node.get(attr)
children = list(node)
for child in children:
result[self._get_key(child.tag)] = self._from_xml_node(
child, listnames)
return result
def default(self, datastring):
return {'body': self._from_xml(datastring)}
def __call__(self, datastring):
# Adding a migration path to allow us to remove unncessary classes
return self.default(datastring)
# NOTE(maru): this class is duplicated from quantum.wsgi
class Serializer(object):
"""Serializes and deserializes dictionaries to certain MIME types."""
def __init__(self, metadata=None, default_xmlns=None):
"""Create a serializer based on the given WSGI environment.
'metadata' is an optional dict mapping MIME types to information
needed to serialize a dictionary to that type.
"""
self.metadata = metadata or {}
self.default_xmlns = default_xmlns
def _get_serialize_handler(self, content_type):
handlers = {
'application/json': JSONDictSerializer(),
'application/xml': XMLDictSerializer(self.metadata),
}
try:
return handlers[content_type]
except Exception:
raise exception.InvalidContentType(content_type=content_type)
def serialize(self, data, content_type):
"""Serialize a dictionary into the specified content type."""
return self._get_serialize_handler(content_type).serialize(data)
def deserialize(self, datastring, content_type):
"""Deserialize a string to a dictionary.
The string must be in the format of a supported MIME type.
"""
return self.get_deserialize_handler(content_type).deserialize(
datastring)
def get_deserialize_handler(self, content_type):
handlers = {
'application/json': JSONDeserializer(),
'application/xml': XMLDeserializer(self.metadata),
}
try:
return handlers[content_type]
except Exception:
raise exception.InvalidContentType(content_type=content_type)

View File

@@ -1,6 +1,7 @@
# vim: tabstop=4 shiftwidth=4 softtabstop=4
# Copyright 2011, Nicira Networks, Inc.
# Copyright 2011 Nicira Networks, Inc
# 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
@@ -13,188 +14,5 @@
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
#
# Borrowed from nova code base, more utilities will be added/borrowed as and
# when needed.
# @author: Somik Behera, Nicira Networks, Inc.
"""Utilities and helper functions."""
import datetime
import json
import logging
import os
import sys
from quantumclient.common import exceptions
from quantumclient.openstack.common import strutils
def env(*vars, **kwargs):
"""Returns the first environment variable set.
if none are non-empty, defaults to '' or keyword arg default.
"""
for v in vars:
value = os.environ.get(v)
if value:
return value
return kwargs.get('default', '')
def to_primitive(value):
if isinstance(value, list) or isinstance(value, tuple):
o = []
for v in value:
o.append(to_primitive(v))
return o
elif isinstance(value, dict):
o = {}
for k, v in value.iteritems():
o[k] = to_primitive(v)
return o
elif isinstance(value, datetime.datetime):
return str(value)
elif hasattr(value, 'iteritems'):
return to_primitive(dict(value.iteritems()))
elif hasattr(value, '__iter__'):
return to_primitive(list(value))
else:
return value
def dumps(value, indent=None):
try:
return json.dumps(value, indent=indent)
except TypeError:
pass
return json.dumps(to_primitive(value))
def loads(s):
return json.loads(s)
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 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 get_item_properties(item, fields, mixed_case_fields=[], formatters={}):
"""Return a tuple containing the item properties.
:param item: a single item resource (e.g. Server, Tenant, etc)
: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 formatters:
row.append(formatters[field](item))
else:
if field in mixed_case_fields:
field_name = field.replace(' ', '_')
else:
field_name = field.lower().replace(' ', '_')
if not hasattr(item, field_name) and isinstance(item, dict):
data = item[field_name]
else:
data = getattr(item, field_name, '')
if data is None:
data = ''
row.append(data)
return tuple(row)
def str2bool(strbool):
if strbool is None:
return None
else:
return strbool.lower() == 'true'
def str2dict(strdict):
'''Convert key1=value1,key2=value2,... string into dictionary.
:param strdict: key1=value1,key2=value2
'''
_info = {}
for kv_str in strdict.split(","):
k, v = kv_str.split("=", 1)
_info.update({k: v})
return _info
def http_log_req(_logger, args, kwargs):
if not _logger.isEnabledFor(logging.DEBUG):
return
string_parts = ['curl -i']
for element in args:
if element in ('GET', 'POST', 'DELETE', 'PUT'):
string_parts.append(' -X %s' % element)
else:
string_parts.append(' %s' % element)
for element in kwargs['headers']:
header = ' -H "%s: %s"' % (element, kwargs['headers'][element])
string_parts.append(header)
if 'body' in kwargs and kwargs['body']:
string_parts.append(" -d '%s'" % (kwargs['body']))
string_parts = safe_encode_list(string_parts)
_logger.debug("\nREQ: %s\n" % "".join(string_parts))
def http_log_resp(_logger, resp, body):
if not _logger.isEnabledFor(logging.DEBUG):
return
_logger.debug("RESP:%s %s\n", resp, body)
def _safe_encode_without_obj(data):
if isinstance(data, basestring):
return strutils.safe_encode(data)
return data
def safe_encode_list(data):
return map(_safe_encode_without_obj, data)
def safe_encode_dict(data):
def _encode_item((k, v)):
if isinstance(v, list):
return (k, safe_encode_list(v))
elif isinstance(v, dict):
return (k, safe_encode_dict(v))
return (k, _safe_encode_without_obj(v))
return dict(map(_encode_item, data.items()))
from neutronclient.common.utils import * # noqa

View File

@@ -1,142 +0,0 @@
# vim: tabstop=4 shiftwidth=4 softtabstop=4
# Copyright 2011 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.
"""
Exceptions common to OpenStack projects
"""
import logging
from quantumclient.openstack.common.gettextutils import _
_FATAL_EXCEPTION_FORMAT_ERRORS = False
class Error(Exception):
def __init__(self, message=None):
super(Error, self).__init__(message)
class ApiError(Error):
def __init__(self, message='Unknown', code='Unknown'):
self.message = message
self.code = code
super(ApiError, self).__init__('%s: %s' % (code, message))
class NotFound(Error):
pass
class UnknownScheme(Error):
msg = "Unknown scheme '%s' found in URI"
def __init__(self, scheme):
msg = self.__class__.msg % scheme
super(UnknownScheme, self).__init__(msg)
class BadStoreUri(Error):
msg = "The Store URI %s was malformed. Reason: %s"
def __init__(self, uri, reason):
msg = self.__class__.msg % (uri, reason)
super(BadStoreUri, self).__init__(msg)
class Duplicate(Error):
pass
class NotAuthorized(Error):
pass
class NotEmpty(Error):
pass
class Invalid(Error):
pass
class BadInputError(Exception):
"""Error resulting from a client sending bad input to a server"""
pass
class MissingArgumentError(Error):
pass
class DatabaseMigrationError(Error):
pass
class ClientConnectionError(Exception):
"""Error resulting from a client connecting to a server"""
pass
def wrap_exception(f):
def _wrap(*args, **kw):
try:
return f(*args, **kw)
except Exception, e:
if not isinstance(e, Error):
#exc_type, exc_value, exc_traceback = sys.exc_info()
logging.exception(_('Uncaught exception'))
#logging.error(traceback.extract_stack(exc_traceback))
raise Error(str(e))
raise
_wrap.func_name = f.func_name
return _wrap
class OpenstackException(Exception):
"""
Base Exception
To correctly use this class, inherit from it and define
a 'message' property. That message will get printf'd
with the keyword arguments provided to the constructor.
"""
message = "An unknown exception occurred"
def __init__(self, **kwargs):
try:
self._error_string = self.message % kwargs
except Exception as e:
if _FATAL_EXCEPTION_FORMAT_ERRORS:
raise e
else:
# at least get the core message out if something happened
self._error_string = self.message
def __str__(self):
return self._error_string
class MalformedRequestBody(OpenstackException):
message = "Malformed message body: %(reason)s"
class InvalidContentType(OpenstackException):
message = "Invalid content type %(content_type)s"

View File

@@ -1,33 +0,0 @@
# vim: tabstop=4 shiftwidth=4 softtabstop=4
# Copyright 2012 Red Hat, Inc.
# 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.
"""
gettext for openstack-common modules.
Usual usage in an openstack.common module:
from quantumclient.openstack.common.gettextutils import _
"""
import gettext
t = gettext.translation('openstack-common', 'locale', fallback=True)
def _(msg):
return t.ugettext(msg)

View File

@@ -1,148 +0,0 @@
# vim: tabstop=4 shiftwidth=4 softtabstop=4
# Copyright 2010 United States Government as represented by the
# Administrator of the National Aeronautics and Space Administration.
# Copyright 2011 Justin Santa Barbara
# 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.
'''
JSON related utilities.
This module provides a few things:
1) A handy function for getting an object down to something that can be
JSON serialized. See to_primitive().
2) Wrappers around loads() and dumps(). The dumps() wrapper will
automatically use to_primitive() for you if needed.
3) This sets up anyjson to use the loads() and dumps() wrappers if anyjson
is available.
'''
import datetime
import inspect
import itertools
import json
import xmlrpclib
from quantumclient.openstack.common import timeutils
def to_primitive(value, convert_instances=False, level=0):
"""Convert a complex object into primitives.
Handy for JSON serialization. We can optionally handle instances,
but since this is a recursive function, we could have cyclical
data structures.
To handle cyclical data structures we could track the actual objects
visited in a set, but not all objects are hashable. Instead we just
track the depth of the object inspections and don't go too deep.
Therefore, convert_instances=True is lossy ... be aware.
"""
nasty = [inspect.ismodule, inspect.isclass, inspect.ismethod,
inspect.isfunction, inspect.isgeneratorfunction,
inspect.isgenerator, inspect.istraceback, inspect.isframe,
inspect.iscode, inspect.isbuiltin, inspect.isroutine,
inspect.isabstract]
for test in nasty:
if test(value):
return unicode(value)
# value of itertools.count doesn't get caught by inspects
# above and results in infinite loop when list(value) is called.
if type(value) == itertools.count:
return unicode(value)
# FIXME(vish): Workaround for LP bug 852095. Without this workaround,
# tests that raise an exception in a mocked method that
# has a @wrap_exception with a notifier will fail. If
# we up the dependency to 0.5.4 (when it is released) we
# can remove this workaround.
if getattr(value, '__module__', None) == 'mox':
return 'mock'
if level > 3:
return '?'
# The try block may not be necessary after the class check above,
# but just in case ...
try:
# It's not clear why xmlrpclib created their own DateTime type, but
# for our purposes, make it a datetime type which is explicitly
# handled
if isinstance(value, xmlrpclib.DateTime):
value = datetime.datetime(*tuple(value.timetuple())[:6])
if isinstance(value, (list, tuple)):
o = []
for v in value:
o.append(to_primitive(v, convert_instances=convert_instances,
level=level))
return o
elif isinstance(value, dict):
o = {}
for k, v in value.iteritems():
o[k] = to_primitive(v, convert_instances=convert_instances,
level=level)
return o
elif isinstance(value, datetime.datetime):
return timeutils.strtime(value)
elif hasattr(value, 'iteritems'):
return to_primitive(dict(value.iteritems()),
convert_instances=convert_instances,
level=level + 1)
elif hasattr(value, '__iter__'):
return to_primitive(list(value),
convert_instances=convert_instances,
level=level)
elif convert_instances and hasattr(value, '__dict__'):
# Likely an instance of something. Watch for cycles.
# Ignore class member vars.
return to_primitive(value.__dict__,
convert_instances=convert_instances,
level=level + 1)
else:
return value
except TypeError:
# Class objects are tricky since they may define something like
# __iter__ defined but it isn't callable as list().
return unicode(value)
def dumps(value, default=to_primitive, **kwargs):
return json.dumps(value, default=default, **kwargs)
def loads(s):
return json.loads(s)
def load(s):
return json.load(s)
try:
import anyjson
except ImportError:
pass
else:
anyjson._modules.append((__name__, 'dumps', TypeError,
'loads', ValueError, 'load'))
anyjson.force_implementation(__name__)

View File

@@ -1,133 +0,0 @@
# vim: tabstop=4 shiftwidth=4 softtabstop=4
# Copyright 2011 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.
"""
System-level utilities and helper functions.
"""
import logging
import sys
LOG = logging.getLogger(__name__)
def int_from_bool_as_string(subject):
"""
Interpret a string as a boolean and return either 1 or 0.
Any string value in:
('True', 'true', 'On', 'on', '1')
is interpreted as a boolean True.
Useful for JSON-decoded stuff and config file parsing
"""
return bool_from_string(subject) and 1 or 0
def bool_from_string(subject):
"""
Interpret a string as a boolean.
Any string value in:
('True', 'true', 'On', 'on', 'Yes', 'yes', '1')
is interpreted as a boolean True.
Useful for JSON-decoded stuff and config file parsing
"""
if isinstance(subject, bool):
return subject
if isinstance(subject, basestring):
if subject.strip().lower() in ('true', 'on', 'yes', '1'):
return True
return False
def safe_decode(text, incoming=None, errors='strict'):
"""
Decodes incoming str using `incoming` if they're
not already unicode.
:param incoming: Text's current encoding
:param errors: Errors handling policy. See here for valid
values http://docs.python.org/2/library/codecs.html
:returns: text or a unicode `incoming` encoded
representation of it.
:raises TypeError: If text is not an isntance of basestring
"""
if not isinstance(text, basestring):
raise TypeError("%s can't be decoded" % type(text))
if isinstance(text, unicode):
return text
if not incoming:
incoming = (sys.stdin.encoding or
sys.getdefaultencoding())
try:
return text.decode(incoming, errors)
except UnicodeDecodeError:
# Note(flaper87) If we get here, it means that
# sys.stdin.encoding / sys.getdefaultencoding
# didn't return a suitable encoding to decode
# text. This happens mostly when global LANG
# var is not set correctly and there's no
# default encoding. In this case, most likely
# python will use ASCII or ANSI encoders as
# default encodings but they won't be capable
# of decoding non-ASCII characters.
#
# Also, UTF-8 is being used since it's an ASCII
# extension.
return text.decode('utf-8', errors)
def safe_encode(text, incoming=None,
encoding='utf-8', errors='strict'):
"""
Encodes incoming str/unicode using `encoding`. If
incoming is not specified, text is expected to
be encoded with current python's default encoding.
(`sys.getdefaultencoding`)
:param incoming: Text's current encoding
:param encoding: Expected encoding for text (Default UTF-8)
:param errors: Errors handling policy. See here for valid
values http://docs.python.org/2/library/codecs.html
:returns: text or a bytestring `encoding` encoded
representation of it.
:raises TypeError: If text is not an isntance of basestring
"""
if not isinstance(text, basestring):
raise TypeError("%s can't be encoded" % type(text))
if not incoming:
incoming = (sys.stdin.encoding or
sys.getdefaultencoding())
if isinstance(text, unicode):
return text.encode(encoding, errors)
elif text and encoding != incoming:
# Decode text before encoding it with `encoding`
text = safe_decode(text, incoming, errors)
return text.encode(encoding, errors)
return text

View File

@@ -1,164 +0,0 @@
# vim: tabstop=4 shiftwidth=4 softtabstop=4
# Copyright 2011 OpenStack LLC.
# 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.
"""
Time related utilities and helper functions.
"""
import calendar
import datetime
import iso8601
TIME_FORMAT = "%Y-%m-%dT%H:%M:%S"
PERFECT_TIME_FORMAT = "%Y-%m-%dT%H:%M:%S.%f"
def isotime(at=None):
"""Stringify time in ISO 8601 format"""
if not at:
at = utcnow()
str = at.strftime(TIME_FORMAT)
tz = at.tzinfo.tzname(None) if at.tzinfo else 'UTC'
str += ('Z' if tz == 'UTC' else tz)
return str
def parse_isotime(timestr):
"""Parse time from ISO 8601 format"""
try:
return iso8601.parse_date(timestr)
except iso8601.ParseError as e:
raise ValueError(e.message)
except TypeError as e:
raise ValueError(e.message)
def strtime(at=None, fmt=PERFECT_TIME_FORMAT):
"""Returns formatted utcnow."""
if not at:
at = utcnow()
return at.strftime(fmt)
def parse_strtime(timestr, fmt=PERFECT_TIME_FORMAT):
"""Turn a formatted time back into a datetime."""
return datetime.datetime.strptime(timestr, fmt)
def normalize_time(timestamp):
"""Normalize time in arbitrary timezone to UTC naive object"""
offset = timestamp.utcoffset()
if offset is None:
return timestamp
return timestamp.replace(tzinfo=None) - offset
def is_older_than(before, seconds):
"""Return True if before is older than seconds."""
if isinstance(before, basestring):
before = parse_strtime(before).replace(tzinfo=None)
return utcnow() - before > datetime.timedelta(seconds=seconds)
def is_newer_than(after, seconds):
"""Return True if after is newer than seconds."""
if isinstance(after, basestring):
after = parse_strtime(after).replace(tzinfo=None)
return after - utcnow() > datetime.timedelta(seconds=seconds)
def utcnow_ts():
"""Timestamp version of our utcnow function."""
return calendar.timegm(utcnow().timetuple())
def utcnow():
"""Overridable version of utils.utcnow."""
if utcnow.override_time:
try:
return utcnow.override_time.pop(0)
except AttributeError:
return utcnow.override_time
return datetime.datetime.utcnow()
utcnow.override_time = None
def set_time_override(override_time=datetime.datetime.utcnow()):
"""
Override utils.utcnow to return a constant time or a list thereof,
one at a time.
"""
utcnow.override_time = override_time
def advance_time_delta(timedelta):
"""Advance overridden time using a datetime.timedelta."""
assert(not utcnow.override_time is None)
try:
for dt in utcnow.override_time:
dt += timedelta
except TypeError:
utcnow.override_time += timedelta
def advance_time_seconds(seconds):
"""Advance overridden time by seconds."""
advance_time_delta(datetime.timedelta(0, seconds))
def clear_time_override():
"""Remove the overridden time."""
utcnow.override_time = None
def marshall_now(now=None):
"""Make an rpc-safe datetime with microseconds.
Note: tzinfo is stripped, but not required for relative times."""
if not now:
now = utcnow()
return dict(day=now.day, month=now.month, year=now.year, hour=now.hour,
minute=now.minute, second=now.second,
microsecond=now.microsecond)
def unmarshall_time(tyme):
"""Unmarshall a datetime dict."""
return datetime.datetime(day=tyme['day'],
month=tyme['month'],
year=tyme['year'],
hour=tyme['hour'],
minute=tyme['minute'],
second=tyme['second'],
microsecond=tyme['microsecond'])
def delta_seconds(before, after):
"""
Compute the difference in seconds between two date, time, or
datetime objects (as a float, to microsecond resolution).
"""
delta = after - before
try:
return delta.total_seconds()
except AttributeError:
return ((delta.days * 24 * 3600) + delta.seconds +
float(delta.microseconds) / (10 ** 6))

View File

@@ -1,64 +0,0 @@
# Copyright 2012 OpenStack LLC.
# 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.
#
# vim: tabstop=4 shiftwidth=4 softtabstop=4
from quantumclient.common import exceptions
from quantumclient.common import utils
API_NAME = 'network'
API_VERSIONS = {
'2.0': 'quantumclient.v2_0.client.Client',
}
def make_client(instance):
"""Returns an quantum client.
"""
quantum_client = utils.get_client_class(
API_NAME,
instance._api_version[API_NAME],
API_VERSIONS,
)
instance.initialize()
url = instance._url
url = url.rstrip("/")
if '2.0' == instance._api_version[API_NAME]:
client = quantum_client(username=instance._username,
tenant_name=instance._tenant_name,
password=instance._password,
region_name=instance._region_name,
auth_url=instance._auth_url,
endpoint_url=url,
token=instance._token,
auth_strategy=instance._auth_strategy,
insecure=instance._insecure)
return client
else:
raise exceptions.UnsupportedVersion("API version %s is not supported" %
instance._api_version[API_NAME])
def Client(api_version, *args, **kwargs):
"""Return an quantum client.
@param api_version: only 2.0 is supported now
"""
quantum_client = utils.get_client_class(
API_NAME,
api_version,
API_VERSIONS,
)
return quantum_client(*args, **kwargs)

View File

@@ -1,4 +1,4 @@
# Copyright 2012 OpenStack LLC.
# Copyright 2013 OpenStack Foundation.
# All Rights Reserved
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
@@ -15,574 +15,6 @@
#
# vim: tabstop=4 shiftwidth=4 softtabstop=4
import argparse
import logging
import re
from neutronclient.neutron.v2_0 import NeutronCommand
from cliff.formatters import table
from cliff import lister
from cliff import show
from quantumclient.common import command
from quantumclient.common import exceptions
from quantumclient.common import utils
from quantumclient.openstack.common.gettextutils import _
HEX_ELEM = '[0-9A-Fa-f]'
UUID_PATTERN = '-'.join([HEX_ELEM + '{8}', HEX_ELEM + '{4}',
HEX_ELEM + '{4}', HEX_ELEM + '{4}',
HEX_ELEM + '{12}'])
def find_resourceid_by_name_or_id(client, resource, name_or_id):
obj_lister = getattr(client, "list_%ss" % resource)
# perform search by id only if we are passing a valid UUID
match = re.match(UUID_PATTERN, name_or_id)
collection = resource + "s"
if match:
data = obj_lister(id=name_or_id, fields='id')
if data and data[collection]:
return data[collection][0]['id']
return _find_resourceid_by_name(client, resource, name_or_id)
def _find_resourceid_by_name(client, resource, name):
obj_lister = getattr(client, "list_%ss" % resource)
data = obj_lister(name=name, fields='id')
collection = resource + "s"
info = data[collection]
if len(info) > 1:
msg = (_("Multiple %(resource)s matches found for name '%(name)s',"
" use an ID to be more specific.") %
{'resource': resource, 'name': name})
raise exceptions.QuantumClientException(
message=msg)
elif len(info) == 0:
not_found_message = (_("Unable to find %(resource)s with name "
"'%(name)s'") %
{'resource': resource, 'name': name})
# 404 is used to simulate server side behavior
raise exceptions.QuantumClientException(
message=not_found_message, status_code=404)
else:
return info[0]['id']
def add_show_list_common_argument(parser):
parser.add_argument(
'-D', '--show-details',
help='show detailed info',
action='store_true',
default=False, )
parser.add_argument(
'--show_details',
action='store_true',
help=argparse.SUPPRESS)
parser.add_argument(
'--fields',
help=argparse.SUPPRESS,
action='append',
default=[])
parser.add_argument(
'-F', '--field',
dest='fields', metavar='FIELD',
help='specify the field(s) to be returned by server,'
' can be repeated',
action='append',
default=[])
def add_pagination_argument(parser):
parser.add_argument(
'-P', '--page-size',
dest='page_size', metavar='SIZE', type=int,
help=("specify retrieve unit of each request, then split one request "
"to several requests"),
default=None)
def add_sorting_argument(parser):
parser.add_argument(
'--sort-key',
dest='sort_key', metavar='FIELD',
action='append',
help=("sort list by specified fields (This option can be repeated), "
"The number of sort_dir and sort_key should match each other, "
"more sort_dir specified will be omitted, less will be filled "
"with asc as default direction "),
default=[])
parser.add_argument(
'--sort-dir',
dest='sort_dir', metavar='{asc,desc}',
help=("sort list in specified directions "
"(This option can be repeated)"),
action='append',
default=[],
choices=['asc', 'desc'])
def is_number(s):
try:
float(s) # for int, long and float
except ValueError:
try:
complex(s) # for complex
except ValueError:
return False
return True
def parse_args_to_dict(values_specs):
'''It is used to analyze the extra command options to command.
Besides known options and arguments, our commands also support user to
put more options to the end of command line. For example,
list_nets -- --tag x y --key1 value1, where '-- --tag x y --key1 value1'
is extra options to our list_nets. This feature can support V2.0 API's
fields selection and filters. For example, to list networks which has name
'test4', we can have list_nets -- --name=test4.
value spec is: --key type=int|bool|... value. Type is one of Python
built-in types. By default, type is string. The key without value is
a bool option. Key with two values will be a list option.
'''
# -- is a pseudo argument
values_specs_copy = values_specs[:]
if values_specs_copy and values_specs_copy[0] == '--':
del values_specs_copy[0]
_options = {}
current_arg = None
_values_specs = []
_value_number = 0
_list_flag = False
current_item = None
for _item in values_specs_copy:
if _item.startswith('--'):
if current_arg is not None:
if _value_number > 1 or _list_flag:
current_arg.update({'nargs': '+'})
elif _value_number == 0:
current_arg.update({'action': 'store_true'})
_temp = _item
if "=" in _item:
_item = _item.split('=')[0]
if _item in _options:
raise exceptions.CommandError(
"duplicated options %s" % ' '.join(values_specs))
else:
_options.update({_item: {}})
current_arg = _options[_item]
_item = _temp
elif _item.startswith('type='):
if current_arg is None:
raise exceptions.CommandError(
"invalid values_specs %s" % ' '.join(values_specs))
if 'type' not in current_arg:
_type_str = _item.split('=', 2)[1]
current_arg.update({'type': eval(_type_str)})
if _type_str == 'bool':
current_arg.update({'type': utils.str2bool})
elif _type_str == 'dict':
current_arg.update({'type': utils.str2dict})
continue
elif _item == 'list=true':
_list_flag = True
continue
if not _item.startswith('--'):
if (not current_item or '=' in current_item or
_item.startswith('-') and not is_number(_item)):
raise exceptions.CommandError(
"Invalid values_specs %s" % ' '.join(values_specs))
_value_number += 1
elif _item.startswith('--'):
current_item = _item
if '=' in current_item:
_value_number = 1
else:
_value_number = 0
_list_flag = False
_values_specs.append(_item)
if current_arg is not None:
if _value_number > 1 or _list_flag:
current_arg.update({'nargs': '+'})
elif _value_number == 0:
current_arg.update({'action': 'store_true'})
_args = None
if _values_specs:
_parser = argparse.ArgumentParser(add_help=False)
for opt, optspec in _options.iteritems():
_parser.add_argument(opt, **optspec)
_args = _parser.parse_args(_values_specs)
result_dict = {}
if _args:
for opt in _options.iterkeys():
_opt = opt.split('--', 2)[1]
_opt = _opt.replace('-', '_')
_value = getattr(_args, _opt)
if _value is not None:
result_dict.update({_opt: _value})
return result_dict
def _merge_args(qCmd, parsed_args, _extra_values, value_specs):
"""Merge arguments from _extra_values into parsed_args.
If an argument value are provided in both and it is a list,
the values in _extra_values will be merged into parsed_args.
@param parsed_args: the parsed args from known options
@param _extra_values: the other parsed arguments in unknown parts
@param values_specs: the unparsed unknown parts
"""
temp_values = _extra_values.copy()
for key, value in temp_values.iteritems():
if hasattr(parsed_args, key):
arg_value = getattr(parsed_args, key)
if arg_value is not None and value is not None:
if isinstance(arg_value, list):
if value and isinstance(value, list):
if type(arg_value[0]) == type(value[0]):
arg_value.extend(value)
_extra_values.pop(key)
def update_dict(obj, dict, attributes):
for attribute in attributes:
if hasattr(obj, attribute) and getattr(obj, attribute):
dict[attribute] = getattr(obj, attribute)
class TableFormater(table.TableFormatter):
"""This class is used to keep consistency with prettytable 0.6.
https://bugs.launchpad.net/python-quantumclient/+bug/1165962
"""
def emit_list(self, column_names, data, stdout, parsed_args):
if column_names:
super(TableFormater, self).emit_list(column_names, data, stdout,
parsed_args)
else:
stdout.write('\n')
class QuantumCommand(command.OpenStackCommand):
api = 'network'
log = logging.getLogger(__name__ + '.QuantumCommand')
values_specs = []
json_indent = None
def __init__(self, app, app_args):
super(QuantumCommand, self).__init__(app, app_args)
if hasattr(self, 'formatters'):
self.formatters['table'] = TableFormater()
def get_client(self):
return self.app.client_manager.quantum
def get_parser(self, prog_name):
parser = super(QuantumCommand, self).get_parser(prog_name)
parser.add_argument(
'--request-format',
help=_('the xml or json request format'),
default='json',
choices=['json', 'xml', ], )
parser.add_argument(
'--request_format',
choices=['json', 'xml', ],
help=argparse.SUPPRESS)
return parser
def format_output_data(self, data):
# Modify data to make it more readable
if self.resource in data:
for k, v in data[self.resource].iteritems():
if isinstance(v, list):
value = '\n'.join(utils.dumps(
i, indent=self.json_indent) if isinstance(i, dict)
else str(i) for i in v)
data[self.resource][k] = value
elif isinstance(v, dict):
value = utils.dumps(v, indent=self.json_indent)
data[self.resource][k] = value
elif v is None:
data[self.resource][k] = ''
def add_known_arguments(self, parser):
pass
def args2body(self, parsed_args):
return {}
class CreateCommand(QuantumCommand, show.ShowOne):
"""Create a resource for a given tenant
"""
api = 'network'
resource = None
log = None
def get_parser(self, prog_name):
parser = super(CreateCommand, self).get_parser(prog_name)
parser.add_argument(
'--tenant-id', metavar='TENANT_ID',
help=_('the owner tenant ID'), )
parser.add_argument(
'--tenant_id',
help=argparse.SUPPRESS)
self.add_known_arguments(parser)
return parser
def get_data(self, parsed_args):
self.log.debug('get_data(%s)' % parsed_args)
quantum_client = self.get_client()
quantum_client.format = parsed_args.request_format
_extra_values = parse_args_to_dict(self.values_specs)
_merge_args(self, parsed_args, _extra_values,
self.values_specs)
body = self.args2body(parsed_args)
body[self.resource].update(_extra_values)
obj_creator = getattr(quantum_client,
"create_%s" % self.resource)
data = obj_creator(body)
self.format_output_data(data)
# {u'network': {u'id': u'e9424a76-6db4-4c93-97b6-ec311cd51f19'}}
info = self.resource in data and data[self.resource] or None
if info:
print >>self.app.stdout, _('Created a new %s:') % self.resource
else:
info = {'': ''}
return zip(*sorted(info.iteritems()))
class UpdateCommand(QuantumCommand):
"""Update resource's information
"""
api = 'network'
resource = None
log = None
def get_parser(self, prog_name):
parser = super(UpdateCommand, self).get_parser(prog_name)
parser.add_argument(
'id', metavar=self.resource.upper(),
help='ID or name of %s to update' % self.resource)
self.add_known_arguments(parser)
return parser
def run(self, parsed_args):
self.log.debug('run(%s)' % parsed_args)
quantum_client = self.get_client()
quantum_client.format = parsed_args.request_format
_extra_values = parse_args_to_dict(self.values_specs)
_merge_args(self, parsed_args, _extra_values,
self.values_specs)
body = self.args2body(parsed_args)
if self.resource in body:
body[self.resource].update(_extra_values)
else:
body[self.resource] = _extra_values
if not body[self.resource]:
raise exceptions.CommandError(
"Must specify new values to update %s" % self.resource)
_id = find_resourceid_by_name_or_id(quantum_client,
self.resource,
parsed_args.id)
obj_updator = getattr(quantum_client,
"update_%s" % self.resource)
obj_updator(_id, body)
print >>self.app.stdout, (
_('Updated %(resource)s: %(id)s') %
{'id': parsed_args.id, 'resource': self.resource})
return
class DeleteCommand(QuantumCommand):
"""Delete a given resource
"""
api = 'network'
resource = None
log = None
allow_names = True
def get_parser(self, prog_name):
parser = super(DeleteCommand, self).get_parser(prog_name)
if self.allow_names:
help_str = 'ID or name of %s to delete'
else:
help_str = 'ID of %s to delete'
parser.add_argument(
'id', metavar=self.resource.upper(),
help=help_str % self.resource)
return parser
def run(self, parsed_args):
self.log.debug('run(%s)' % parsed_args)
quantum_client = self.get_client()
quantum_client.format = parsed_args.request_format
obj_deleter = getattr(quantum_client,
"delete_%s" % self.resource)
if self.allow_names:
_id = find_resourceid_by_name_or_id(quantum_client, self.resource,
parsed_args.id)
else:
_id = parsed_args.id
obj_deleter(_id)
print >>self.app.stdout, (_('Deleted %(resource)s: %(id)s')
% {'id': parsed_args.id,
'resource': self.resource})
return
class ListCommand(QuantumCommand, lister.Lister):
"""List resources that belong to a given tenant
"""
api = 'network'
resource = None
log = None
_formatters = {}
list_columns = []
unknown_parts_flag = True
pagination_support = False
sorting_support = False
def get_parser(self, prog_name):
parser = super(ListCommand, self).get_parser(prog_name)
add_show_list_common_argument(parser)
if self.pagination_support:
add_pagination_argument(parser)
if self.sorting_support:
add_sorting_argument(parser)
return parser
def args2search_opts(self, parsed_args):
search_opts = {}
fields = parsed_args.fields
if parsed_args.fields:
search_opts.update({'fields': fields})
if parsed_args.show_details:
search_opts.update({'verbose': 'True'})
return search_opts
def call_server(self, quantum_client, search_opts, parsed_args):
obj_lister = getattr(quantum_client,
"list_%ss" % self.resource)
data = obj_lister(**search_opts)
return data
def retrieve_list(self, parsed_args):
"""Retrieve a list of resources from Quantum server"""
quantum_client = self.get_client()
quantum_client.format = parsed_args.request_format
_extra_values = parse_args_to_dict(self.values_specs)
_merge_args(self, parsed_args, _extra_values,
self.values_specs)
search_opts = self.args2search_opts(parsed_args)
search_opts.update(_extra_values)
if self.pagination_support:
page_size = parsed_args.page_size
if page_size:
search_opts.update({'limit': page_size})
if self.sorting_support:
keys = parsed_args.sort_key
if keys:
search_opts.update({'sort_key': keys})
dirs = parsed_args.sort_dir
len_diff = len(keys) - len(dirs)
if len_diff > 0:
dirs += ['asc'] * len_diff
elif len_diff < 0:
dirs = dirs[:len(keys)]
if dirs:
search_opts.update({'sort_dir': dirs})
data = self.call_server(quantum_client, search_opts, parsed_args)
collection = self.resource + "s"
return data.get(collection, [])
def extend_list(self, data, parsed_args):
"""Update a retrieved list.
This method provides a way to modify a original list returned from
the quantum server. For example, you can add subnet cidr information
to a list network.
"""
pass
def setup_columns(self, info, parsed_args):
_columns = len(info) > 0 and sorted(info[0].keys()) or []
if not _columns:
# clean the parsed_args.columns so that cliff will not break
parsed_args.columns = []
elif parsed_args.columns:
_columns = [x for x in parsed_args.columns if x in _columns]
elif self.list_columns:
# if no -c(s) by user and list_columns, we use columns in
# both list_columns and returned resource.
# Also Keep their order the same as in list_columns
_columns = [x for x in self.list_columns if x in _columns]
return (_columns, (utils.get_item_properties(
s, _columns, formatters=self._formatters, )
for s in info), )
def get_data(self, parsed_args):
self.log.debug('get_data(%s)' % parsed_args)
data = self.retrieve_list(parsed_args)
self.extend_list(data, parsed_args)
return self.setup_columns(data, parsed_args)
class ShowCommand(QuantumCommand, show.ShowOne):
"""Show information of a given resource
"""
api = 'network'
resource = None
log = None
allow_names = True
def get_parser(self, prog_name):
parser = super(ShowCommand, self).get_parser(prog_name)
add_show_list_common_argument(parser)
if self.allow_names:
help_str = 'ID or name of %s to look up'
else:
help_str = 'ID of %s to look up'
parser.add_argument(
'id', metavar=self.resource.upper(),
help=help_str % self.resource)
return parser
def get_data(self, parsed_args):
self.log.debug('get_data(%s)' % parsed_args)
quantum_client = self.get_client()
quantum_client.format = parsed_args.request_format
params = {}
if parsed_args.show_details:
params = {'verbose': 'True'}
if parsed_args.fields:
params = {'fields': parsed_args.fields}
if self.allow_names:
_id = find_resourceid_by_name_or_id(quantum_client, self.resource,
parsed_args.id)
else:
_id = parsed_args.id
obj_shower = getattr(quantum_client, "show_%s" % self.resource)
data = obj_shower(_id, **params)
self.format_output_data(data)
resource = data[self.resource]
if self.resource in data:
return zip(*sorted(resource.iteritems()))
else:
return None
QuantumCommand = NeutronCommand

View File

@@ -1,65 +0,0 @@
# Copyright 2013 OpenStack LLC.
# 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.
#
# vim: tabstop=4 shiftwidth=4 softtabstop=4
import logging
from quantumclient.quantum import v2_0 as quantumV20
def _format_timestamp(component):
try:
return component['heartbeat_timestamp'].split(".", 2)[0]
except Exception:
return ''
class ListAgent(quantumV20.ListCommand):
"""List agents."""
resource = 'agent'
log = logging.getLogger(__name__ + '.ListAgent')
list_columns = ['id', 'agent_type', 'host', 'alive', 'admin_state_up']
_formatters = {'heartbeat_timestamp': _format_timestamp}
def extend_list(self, data, parsed_args):
for agent in data:
agent['alive'] = ":-)" if agent['alive'] else 'xxx'
class ShowAgent(quantumV20.ShowCommand):
"""Show information of a given agent."""
resource = 'agent'
log = logging.getLogger(__name__ + '.ShowAgent')
allow_names = False
json_indent = 5
class DeleteAgent(quantumV20.DeleteCommand):
"""Delete a given agent."""
log = logging.getLogger(__name__ + '.DeleteAgent')
resource = 'agent'
allow_names = False
class UpdateAgent(quantumV20.UpdateCommand):
"""Update a given agent."""
log = logging.getLogger(__name__ + '.UpdateAgent')
resource = 'agent'
allow_names = False

View File

@@ -1,234 +0,0 @@
# Copyright 2013 OpenStack LLC.
# 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.
#
# vim: tabstop=4 shiftwidth=4 softtabstop=4
import logging
from quantumclient.openstack.common.gettextutils import _
from quantumclient.quantum import v2_0 as quantumV20
from quantumclient.quantum.v2_0 import network
from quantumclient.quantum.v2_0 import router
PERFECT_TIME_FORMAT = "%Y-%m-%dT%H:%M:%S.%f"
class AddNetworkToDhcpAgent(quantumV20.QuantumCommand):
"""Add a network to a DHCP agent."""
log = logging.getLogger(__name__ + '.AddNetworkToDhcpAgent')
def get_parser(self, prog_name):
parser = super(AddNetworkToDhcpAgent, self).get_parser(prog_name)
parser.add_argument(
'dhcp_agent',
help='ID of the DHCP agent')
parser.add_argument(
'network',
help='network to add')
return parser
def run(self, parsed_args):
self.log.debug('run(%s)' % parsed_args)
quantum_client = self.get_client()
quantum_client.format = parsed_args.request_format
_net_id = quantumV20.find_resourceid_by_name_or_id(
quantum_client, 'network', parsed_args.network)
quantum_client.add_network_to_dhcp_agent(parsed_args.dhcp_agent,
{'network_id': _net_id})
print >>self.app.stdout, (
_('Added network %s to DHCP agent') % parsed_args.network)
class RemoveNetworkFromDhcpAgent(quantumV20.QuantumCommand):
"""Remove a network from a DHCP agent."""
log = logging.getLogger(__name__ + '.RemoveNetworkFromDhcpAgent')
def get_parser(self, prog_name):
parser = super(RemoveNetworkFromDhcpAgent, self).get_parser(prog_name)
parser.add_argument(
'dhcp_agent',
help='ID of the DHCP agent')
parser.add_argument(
'network',
help='network to remove')
return parser
def run(self, parsed_args):
self.log.debug('run(%s)' % parsed_args)
quantum_client = self.get_client()
quantum_client.format = parsed_args.request_format
_net_id = quantumV20.find_resourceid_by_name_or_id(
quantum_client, 'network', parsed_args.network)
quantum_client.remove_network_from_dhcp_agent(
parsed_args.dhcp_agent, _net_id)
print >>self.app.stdout, (
_('Removed network %s to DHCP agent') % parsed_args.network)
class ListNetworksOnDhcpAgent(network.ListNetwork):
"""List the networks on a DHCP agent."""
log = logging.getLogger(__name__ + '.ListNetworksOnDhcpAgent')
unknown_parts_flag = False
def get_parser(self, prog_name):
parser = super(ListNetworksOnDhcpAgent,
self).get_parser(prog_name)
parser.add_argument(
'dhcp_agent',
help='ID of the DHCP agent')
return parser
def call_server(self, quantum_client, search_opts, parsed_args):
data = quantum_client.list_networks_on_dhcp_agent(
parsed_args.dhcp_agent, **search_opts)
return data
class ListDhcpAgentsHostingNetwork(quantumV20.ListCommand):
"""List DHCP agents hosting a network."""
resource = 'agent'
_formatters = {}
log = logging.getLogger(__name__ + '.ListDhcpAgentsHostingNetwork')
list_columns = ['id', 'host', 'admin_state_up', 'alive']
unknown_parts_flag = False
def get_parser(self, prog_name):
parser = super(ListDhcpAgentsHostingNetwork,
self).get_parser(prog_name)
parser.add_argument(
'network',
help='network to query')
return parser
def extend_list(self, data, parsed_args):
for agent in data:
agent['alive'] = ":-)" if agent['alive'] else 'xxx'
def call_server(self, quantum_client, search_opts, parsed_args):
_id = quantumV20.find_resourceid_by_name_or_id(quantum_client,
'network',
parsed_args.network)
search_opts['network'] = _id
data = quantum_client.list_dhcp_agent_hosting_networks(**search_opts)
return data
class AddRouterToL3Agent(quantumV20.QuantumCommand):
"""Add a router to a L3 agent."""
log = logging.getLogger(__name__ + '.AddRouterToL3Agent')
def get_parser(self, prog_name):
parser = super(AddRouterToL3Agent, self).get_parser(prog_name)
parser.add_argument(
'l3_agent',
help='ID of the L3 agent')
parser.add_argument(
'router',
help='router to add')
return parser
def run(self, parsed_args):
self.log.debug('run(%s)' % parsed_args)
quantum_client = self.get_client()
quantum_client.format = parsed_args.request_format
_id = quantumV20.find_resourceid_by_name_or_id(
quantum_client, 'router', parsed_args.router)
quantum_client.add_router_to_l3_agent(parsed_args.l3_agent,
{'router_id': _id})
print >>self.app.stdout, (
_('Added router %s to L3 agent') % parsed_args.router)
class RemoveRouterFromL3Agent(quantumV20.QuantumCommand):
"""Remove a router from a L3 agent."""
log = logging.getLogger(__name__ + '.RemoveRouterFromL3Agent')
def get_parser(self, prog_name):
parser = super(RemoveRouterFromL3Agent, self).get_parser(prog_name)
parser.add_argument(
'l3_agent',
help='ID of the L3 agent')
parser.add_argument(
'router',
help='router to remove')
return parser
def run(self, parsed_args):
self.log.debug('run(%s)' % parsed_args)
quantum_client = self.get_client()
quantum_client.format = parsed_args.request_format
_id = quantumV20.find_resourceid_by_name_or_id(
quantum_client, 'router', parsed_args.router)
quantum_client.remove_router_from_l3_agent(
parsed_args.l3_agent, _id)
print >>self.app.stdout, (
_('Removed Router %s to L3 agent') % parsed_args.router)
class ListRoutersOnL3Agent(quantumV20.ListCommand):
"""List the routers on a L3 agent."""
log = logging.getLogger(__name__ + '.ListRoutersOnL3Agent')
_formatters = {'external_gateway_info':
router._format_external_gateway_info}
list_columns = ['id', 'name', 'external_gateway_info']
resource = 'router'
unknown_parts_flag = False
def get_parser(self, prog_name):
parser = super(ListRoutersOnL3Agent,
self).get_parser(prog_name)
parser.add_argument(
'l3_agent',
help='ID of the L3 agent to query')
return parser
def call_server(self, quantum_client, search_opts, parsed_args):
data = quantum_client.list_routers_on_l3_agent(
parsed_args.l3_agent, **search_opts)
return data
class ListL3AgentsHostingRouter(quantumV20.ListCommand):
"""List L3 agents hosting a router."""
resource = 'agent'
_formatters = {}
log = logging.getLogger(__name__ + '.ListL3AgentsHostingRouter')
list_columns = ['id', 'host', 'admin_state_up', 'alive', 'default']
unknown_parts_flag = False
def get_parser(self, prog_name):
parser = super(ListL3AgentsHostingRouter,
self).get_parser(prog_name)
parser.add_argument('router',
help='router to query')
return parser
def extend_list(self, data, parsed_args):
for agent in data:
agent['alive'] = ":-)" if agent['alive'] else 'xxx'
def call_server(self, quantum_client, search_opts, parsed_args):
_id = quantumV20.find_resourceid_by_name_or_id(quantum_client,
'router',
parsed_args.router)
search_opts['router'] = _id
data = quantum_client.list_l3_agent_hosting_routers(**search_opts)
return data

View File

@@ -1,44 +0,0 @@
# Copyright 2012 OpenStack LLC.
# 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.
#
# vim: tabstop=4 shiftwidth=4 softtabstop=4
import logging
from quantumclient.quantum import v2_0 as cmd_base
class ListExt(cmd_base.ListCommand):
"""List all extensions."""
resource = 'extension'
log = logging.getLogger(__name__ + '.ListExt')
list_columns = ['alias', 'name']
class ShowExt(cmd_base.ShowCommand):
"""Show information of a given resource."""
resource = "extension"
log = logging.getLogger(__name__ + '.ShowExt')
allow_names = False
def get_parser(self, prog_name):
parser = super(cmd_base.ShowCommand, self).get_parser(prog_name)
cmd_base.add_show_list_common_argument(parser)
parser.add_argument(
'id', metavar='EXT-ALIAS',
help='the extension alias')
return parser

View File

@@ -1,151 +0,0 @@
# Copyright 2012 OpenStack LLC.
# 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.
#
# vim: tabstop=4 shiftwidth=4 softtabstop=4
import argparse
import logging
from quantumclient.openstack.common.gettextutils import _
from quantumclient.quantum import v2_0 as quantumv20
class ListFloatingIP(quantumv20.ListCommand):
"""List floating ips that belong to a given tenant."""
resource = 'floatingip'
log = logging.getLogger(__name__ + '.ListFloatingIP')
list_columns = ['id', 'fixed_ip_address', 'floating_ip_address',
'port_id']
pagination_support = True
sorting_support = True
class ShowFloatingIP(quantumv20.ShowCommand):
"""Show information of a given floating ip."""
resource = 'floatingip'
log = logging.getLogger(__name__ + '.ShowFloatingIP')
allow_names = False
class CreateFloatingIP(quantumv20.CreateCommand):
"""Create a floating ip for a given tenant."""
resource = 'floatingip'
log = logging.getLogger(__name__ + '.CreateFloatingIP')
def add_known_arguments(self, parser):
parser.add_argument(
'floating_network_id', metavar='FLOATING_NETWORK',
help='Network name or id to allocate floating IP from')
parser.add_argument(
'--port-id',
help='ID of the port to be associated with the floatingip')
parser.add_argument(
'--port_id',
help=argparse.SUPPRESS)
parser.add_argument(
'--fixed-ip-address',
help=('IP address on the port (only required if port has multiple'
'IPs)'))
parser.add_argument(
'--fixed_ip_address',
help=argparse.SUPPRESS)
def args2body(self, parsed_args):
_network_id = quantumv20.find_resourceid_by_name_or_id(
self.get_client(), 'network', parsed_args.floating_network_id)
body = {self.resource: {'floating_network_id': _network_id}}
if parsed_args.port_id:
body[self.resource].update({'port_id': parsed_args.port_id})
if parsed_args.tenant_id:
body[self.resource].update({'tenant_id': parsed_args.tenant_id})
if parsed_args.fixed_ip_address:
body[self.resource].update({'fixed_ip_address':
parsed_args.fixed_ip_address})
return body
class DeleteFloatingIP(quantumv20.DeleteCommand):
"""Delete a given floating ip."""
log = logging.getLogger(__name__ + '.DeleteFloatingIP')
resource = 'floatingip'
allow_names = False
class AssociateFloatingIP(quantumv20.QuantumCommand):
"""Create a mapping between a floating ip and a fixed ip."""
api = 'network'
log = logging.getLogger(__name__ + '.AssociateFloatingIP')
resource = 'floatingip'
def get_parser(self, prog_name):
parser = super(AssociateFloatingIP, self).get_parser(prog_name)
parser.add_argument(
'floatingip_id', metavar='FLOATINGIP_ID',
help='ID of the floating IP to associate')
parser.add_argument(
'port_id', metavar='PORT',
help='ID or name of the port to be associated with the floatingip')
parser.add_argument(
'--fixed-ip-address',
help=('IP address on the port (only required if port has multiple'
'IPs)'))
parser.add_argument(
'--fixed_ip_address',
help=argparse.SUPPRESS)
return parser
def run(self, parsed_args):
self.log.debug('run(%s)' % parsed_args)
quantum_client = self.get_client()
quantum_client.format = parsed_args.request_format
update_dict = {}
if parsed_args.port_id:
update_dict['port_id'] = parsed_args.port_id
if parsed_args.fixed_ip_address:
update_dict['fixed_ip_address'] = parsed_args.fixed_ip_address
quantum_client.update_floatingip(parsed_args.floatingip_id,
{'floatingip': update_dict})
print >>self.app.stdout, (
_('Associated floatingip %s') % parsed_args.floatingip_id)
class DisassociateFloatingIP(quantumv20.QuantumCommand):
"""Remove a mapping from a floating ip to a fixed ip.
"""
api = 'network'
log = logging.getLogger(__name__ + '.DisassociateFloatingIP')
resource = 'floatingip'
def get_parser(self, prog_name):
parser = super(DisassociateFloatingIP, self).get_parser(prog_name)
parser.add_argument(
'floatingip_id', metavar='FLOATINGIP_ID',
help='ID of the floating IP to associate')
return parser
def run(self, parsed_args):
self.log.debug('run(%s)' % parsed_args)
quantum_client = self.get_client()
quantum_client.format = parsed_args.request_format
quantum_client.update_floatingip(parsed_args.floatingip_id,
{'floatingip': {'port_id': None}})
print >>self.app.stdout, (
_('Disassociated floatingip %s') % parsed_args.floatingip_id)

View File

@@ -1,16 +0,0 @@
# Copyright 2013 OpenStack LLC.
# 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.
#
# vim: tabstop=4 shiftwidth=4 softtabstop=4

View File

@@ -1,174 +0,0 @@
# Copyright 2013 Mirantis Inc.
# 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.
#
# @author: Ilya Shakhat, Mirantis Inc.
#
# vim: tabstop=4 shiftwidth=4 softtabstop=4
import logging
from quantumclient.openstack.common.gettextutils import _
from quantumclient.quantum import v2_0 as quantumv20
class ListHealthMonitor(quantumv20.ListCommand):
"""List healthmonitors that belong to a given tenant."""
resource = 'health_monitor'
log = logging.getLogger(__name__ + '.ListHealthMonitor')
list_columns = ['id', 'type', 'admin_state_up', 'status']
pagination_support = True
sorting_support = True
class ShowHealthMonitor(quantumv20.ShowCommand):
"""Show information of a given healthmonitor."""
resource = 'health_monitor'
log = logging.getLogger(__name__ + '.ShowHealthMonitor')
class CreateHealthMonitor(quantumv20.CreateCommand):
"""Create a healthmonitor."""
resource = 'health_monitor'
log = logging.getLogger(__name__ + '.CreateHealthMonitor')
def add_known_arguments(self, parser):
parser.add_argument(
'--admin-state-down',
dest='admin_state', action='store_false',
help='set admin state up to false')
parser.add_argument(
'--expected-codes',
help='the list of HTTP status codes expected in '
'response from the member to declare it healthy. This '
'attribute can contain one value, '
'or a list of values separated by comma, '
'or a range of values (e.g. "200-299"). If this attribute '
'is not specified, it defaults to "200". ')
parser.add_argument(
'--http-method',
help='the HTTP method used for requests by the monitor of type '
'HTTP.')
parser.add_argument(
'--url-path',
help='the HTTP path used in the HTTP request used by the monitor'
' to test a member health. This must be a string '
'beginning with a / (forward slash)')
parser.add_argument(
'--delay',
required=True,
help='the minimum time in seconds between regular connections '
'of the member.')
parser.add_argument(
'--max-retries',
required=True,
help='number of permissible connection failures before changing '
'the member status to INACTIVE.')
parser.add_argument(
'--timeout',
required=True,
help='maximum number of seconds for a monitor to wait for a '
'connection to be established before it times out. The '
'value must be less than the delay value.')
parser.add_argument(
'--type',
required=True,
help='one of predefined health monitor types, e.g. RoundRobin')
def args2body(self, parsed_args):
body = {
self.resource: {
'admin_state_up': parsed_args.admin_state,
'delay': parsed_args.delay,
'max_retries': parsed_args.max_retries,
'timeout': parsed_args.timeout,
'type': parsed_args.type,
},
}
quantumv20.update_dict(parsed_args, body[self.resource],
['expected_codes', 'http_method', 'url_path',
'tenant_id'])
return body
class UpdateHealthMonitor(quantumv20.UpdateCommand):
"""Update a given healthmonitor."""
resource = 'health_monitor'
log = logging.getLogger(__name__ + '.UpdateHealthMonitor')
class DeleteHealthMonitor(quantumv20.DeleteCommand):
"""Delete a given healthmonitor."""
resource = 'health_monitor'
log = logging.getLogger(__name__ + '.DeleteHealthMonitor')
class AssociateHealthMonitor(quantumv20.QuantumCommand):
"""Create a mapping between a health monitor and a pool."""
log = logging.getLogger(__name__ + '.AssociateHealthMonitor')
resource = 'health_monitor'
def get_parser(self, prog_name):
parser = super(AssociateHealthMonitor, self).get_parser(prog_name)
parser.add_argument(
'health_monitor_id',
help='Health monitor to associate')
parser.add_argument(
'pool_id',
help='ID of the pool to be associated with the health monitor')
return parser
def run(self, parsed_args):
quantum_client = self.get_client()
quantum_client.format = parsed_args.request_format
body = {'health_monitor': {'id': parsed_args.health_monitor_id}}
pool_id = quantumv20.find_resourceid_by_name_or_id(
quantum_client, 'pool', parsed_args.pool_id)
quantum_client.associate_health_monitor(pool_id, body)
print >>self.app.stdout, (_('Associated health monitor '
'%s') % parsed_args.health_monitor_id)
class DisassociateHealthMonitor(quantumv20.QuantumCommand):
"""Remove a mapping from a health monitor to a pool."""
log = logging.getLogger(__name__ + '.DisassociateHealthMonitor')
resource = 'health_monitor'
def get_parser(self, prog_name):
parser = super(DisassociateHealthMonitor, self).get_parser(prog_name)
parser.add_argument(
'health_monitor_id',
help='Health monitor to associate')
parser.add_argument(
'pool_id',
help='ID of the pool to be associated with the health monitor')
return parser
def run(self, parsed_args):
quantum_client = self.get_client()
quantum_client.format = parsed_args.request_format
pool_id = quantumv20.find_resourceid_by_name_or_id(
quantum_client, 'pool', parsed_args.pool_id)
quantum_client.disassociate_health_monitor(pool_id,
parsed_args
.health_monitor_id)
print >>self.app.stdout, (_('Disassociated health monitor '
'%s') % parsed_args.health_monitor_id)

View File

@@ -1,99 +0,0 @@
# Copyright 2013 Mirantis Inc.
# 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.
#
# @author: Ilya Shakhat, Mirantis Inc.
#
# vim: tabstop=4 shiftwidth=4 softtabstop=4
import logging
from quantumclient.quantum import v2_0 as quantumv20
class ListMember(quantumv20.ListCommand):
"""List members that belong to a given tenant."""
resource = 'member'
log = logging.getLogger(__name__ + '.ListMember')
list_columns = [
'id', 'address', 'protocol_port', 'admin_state_up', 'status'
]
pagination_support = True
sorting_support = True
class ShowMember(quantumv20.ShowCommand):
"""Show information of a given member."""
resource = 'member'
log = logging.getLogger(__name__ + '.ShowMember')
class CreateMember(quantumv20.CreateCommand):
"""Create a member."""
resource = 'member'
log = logging.getLogger(__name__ + '.CreateMember')
def add_known_arguments(self, parser):
parser.add_argument(
'pool_id', metavar='pool',
help='Pool id or name this vip belongs to')
parser.add_argument(
'--admin-state-down',
dest='admin_state', action='store_false',
help='set admin state up to false')
parser.add_argument(
'--weight',
help='weight of pool member in the pool')
parser.add_argument(
'--address',
required=True,
help='IP address of the pool member on the pool network. ')
parser.add_argument(
'--protocol-port',
required=True,
help='port on which the pool member listens for requests or '
'connections. ')
def args2body(self, parsed_args):
_pool_id = quantumv20.find_resourceid_by_name_or_id(
self.get_client(), 'pool', parsed_args.pool_id)
body = {
self.resource: {
'pool_id': _pool_id,
'admin_state_up': parsed_args.admin_state,
},
}
quantumv20.update_dict(
parsed_args,
body[self.resource],
['address', 'protocol_port', 'weight', 'tenant_id']
)
return body
class UpdateMember(quantumv20.UpdateCommand):
"""Update a given member."""
resource = 'member'
log = logging.getLogger(__name__ + '.UpdateMember')
class DeleteMember(quantumv20.DeleteCommand):
"""Delete a given member."""
resource = 'member'
log = logging.getLogger(__name__ + '.DeleteMember')

View File

@@ -1,124 +0,0 @@
# Copyright 2013 Mirantis Inc.
# 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.
#
# @author: Ilya Shakhat, Mirantis Inc.
#
# vim: tabstop=4 shiftwidth=4 softtabstop=4
import logging
from quantumclient.quantum import v2_0 as quantumv20
class ListPool(quantumv20.ListCommand):
"""List pools that belong to a given tenant."""
resource = 'pool'
log = logging.getLogger(__name__ + '.ListPool')
list_columns = ['id', 'name', 'lb_method', 'protocol',
'admin_state_up', 'status']
pagination_support = True
sorting_support = True
class ShowPool(quantumv20.ShowCommand):
"""Show information of a given pool."""
resource = 'pool'
log = logging.getLogger(__name__ + '.ShowPool')
class CreatePool(quantumv20.CreateCommand):
"""Create a pool."""
resource = 'pool'
log = logging.getLogger(__name__ + '.CreatePool')
def add_known_arguments(self, parser):
parser.add_argument(
'--admin-state-down',
dest='admin_state', action='store_false',
help='set admin state up to false')
parser.add_argument(
'--description',
help='description of the pool')
parser.add_argument(
'--lb-method',
required=True,
help='the algorithm used to distribute load between the members '
'of the pool')
parser.add_argument(
'--name',
required=True,
help='the name of the pool')
parser.add_argument(
'--protocol',
required=True,
help='protocol for balancing')
parser.add_argument(
'--subnet-id',
required=True,
help='the subnet on which the members of the pool will be located')
def args2body(self, parsed_args):
_subnet_id = quantumv20.find_resourceid_by_name_or_id(
self.get_client(), 'subnet', parsed_args.subnet_id)
body = {
self.resource: {
'admin_state_up': parsed_args.admin_state,
'subnet_id': _subnet_id,
},
}
quantumv20.update_dict(parsed_args, body[self.resource],
['description', 'lb_method', 'name',
'protocol', 'tenant_id'])
return body
class UpdatePool(quantumv20.UpdateCommand):
"""Update a given pool."""
resource = 'pool'
log = logging.getLogger(__name__ + '.UpdatePool')
class DeletePool(quantumv20.DeleteCommand):
"""Delete a given pool."""
resource = 'pool'
log = logging.getLogger(__name__ + '.DeletePool')
class RetrievePoolStats(quantumv20.ShowCommand):
"""Retrieve stats for a given pool."""
resource = 'pool'
log = logging.getLogger(__name__ + '.RetrievePoolStats')
def get_data(self, parsed_args):
self.log.debug('run(%s)' % parsed_args)
quantum_client = self.get_client()
quantum_client.format = parsed_args.request_format
params = {}
if parsed_args.fields:
params = {'fields': parsed_args.fields}
data = quantum_client.retrieve_pool_stats(parsed_args.id, **params)
self.format_output_data(data)
stats = data['stats']
if 'stats' in data:
return zip(*sorted(stats.iteritems()))
else:
return None

View File

@@ -1,115 +0,0 @@
# Copyright 2013 Mirantis Inc.
# 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.
#
# @author: Ilya Shakhat, Mirantis Inc.
#
# vim: tabstop=4 shiftwidth=4 softtabstop=4
import logging
from quantumclient.quantum import v2_0 as quantumv20
class ListVip(quantumv20.ListCommand):
"""List vips that belong to a given tenant."""
resource = 'vip'
log = logging.getLogger(__name__ + '.ListVip')
list_columns = ['id', 'name', 'algorithm', 'address', 'protocol',
'admin_state_up', 'status']
pagination_support = True
sorting_support = True
class ShowVip(quantumv20.ShowCommand):
"""Show information of a given vip."""
resource = 'vip'
log = logging.getLogger(__name__ + '.ShowVip')
class CreateVip(quantumv20.CreateCommand):
"""Create a vip."""
resource = 'vip'
log = logging.getLogger(__name__ + '.CreateVip')
def add_known_arguments(self, parser):
parser.add_argument(
'pool_id', metavar='pool',
help='Pool id or name this vip belongs to')
parser.add_argument(
'--address',
help='IP address of the vip')
parser.add_argument(
'--admin-state-down',
dest='admin_state', action='store_false',
help='set admin state up to false')
parser.add_argument(
'--connection-limit',
help='the maximum number of connections per second allowed for '
'the vip')
parser.add_argument(
'--description',
help='description of the vip')
parser.add_argument(
'--name',
required=True,
help='name of the vip')
parser.add_argument(
'--protocol-port',
required=True,
help='TCP port on which to listen for client traffic that is '
'associated with the vip address')
parser.add_argument(
'--protocol',
required=True,
help='protocol for balancing')
parser.add_argument(
'--subnet-id',
required=True,
help='the subnet on which to allocate the vip address')
def args2body(self, parsed_args):
_pool_id = quantumv20.find_resourceid_by_name_or_id(
self.get_client(), 'pool', parsed_args.pool_id)
_subnet_id = quantumv20.find_resourceid_by_name_or_id(
self.get_client(), 'subnet', parsed_args.subnet_id)
body = {
self.resource: {
'pool_id': _pool_id,
'admin_state_up': parsed_args.admin_state,
'subnet_id': _subnet_id,
},
}
quantumv20.update_dict(parsed_args, body[self.resource],
['address', 'connection_limit', 'description',
'name', 'protocol_port', 'protocol',
'tenant_id'])
return body
class UpdateVip(quantumv20.UpdateCommand):
"""Update a given vip."""
resource = 'vip'
log = logging.getLogger(__name__ + '.UpdateVip')
class DeleteVip(quantumv20.DeleteCommand):
"""Delete a given vip."""
resource = 'vip'
log = logging.getLogger(__name__ + '.DeleteVip')

View File

@@ -1,152 +0,0 @@
# Copyright 2012 OpenStack LLC.
# 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.
#
# vim: tabstop=4 shiftwidth=4 softtabstop=4
import argparse
import logging
from quantumclient.common import exceptions
from quantumclient.quantum import v2_0 as quantumv20
def _format_subnets(network):
try:
return '\n'.join([' '.join([s['id'], s.get('cidr', '')])
for s in network['subnets']])
except Exception:
return ''
class ListNetwork(quantumv20.ListCommand):
"""List networks that belong to a given tenant."""
# Length of a query filter on subnet id
# id=<uuid>& (with len(uuid)=36)
subnet_id_filter_len = 40
resource = 'network'
log = logging.getLogger(__name__ + '.ListNetwork')
_formatters = {'subnets': _format_subnets, }
list_columns = ['id', 'name', 'subnets']
pagination_support = True
sorting_support = True
def extend_list(self, data, parsed_args):
"""Add subnet information to a network list."""
quantum_client = self.get_client()
search_opts = {'fields': ['id', 'cidr']}
if self.pagination_support:
page_size = parsed_args.page_size
if page_size:
search_opts.update({'limit': page_size})
subnet_ids = []
for n in data:
if 'subnets' in n:
subnet_ids.extend(n['subnets'])
def _get_subnet_list(sub_ids):
search_opts['id'] = sub_ids
return quantum_client.list_subnets(
**search_opts).get('subnets', [])
try:
subnets = _get_subnet_list(subnet_ids)
except exceptions.RequestURITooLong as uri_len_exc:
# The URI is too long because of too many subnet_id filters
# Use the excess attribute of the exception to know how many
# subnet_id filters can be inserted into a single request
subnet_count = len(subnet_ids)
max_size = ((self.subnet_id_filter_len * subnet_count) -
uri_len_exc.excess)
chunk_size = max_size / self.subnet_id_filter_len
subnets = []
for i in xrange(0, subnet_count, chunk_size):
subnets.extend(
_get_subnet_list(subnet_ids[i: i + chunk_size]))
subnet_dict = dict([(s['id'], s) for s in subnets])
for n in data:
if 'subnets' in n:
n['subnets'] = [(subnet_dict.get(s) or {"id": s})
for s in n['subnets']]
class ListExternalNetwork(ListNetwork):
"""List external networks that belong to a given tenant."""
log = logging.getLogger(__name__ + '.ListExternalNetwork')
pagination_support = True
sorting_support = True
def retrieve_list(self, parsed_args):
external = '--router:external=True'
if external not in self.values_specs:
self.values_specs.append('--router:external=True')
return super(ListExternalNetwork, self).retrieve_list(parsed_args)
class ShowNetwork(quantumv20.ShowCommand):
"""Show information of a given network."""
resource = 'network'
log = logging.getLogger(__name__ + '.ShowNetwork')
class CreateNetwork(quantumv20.CreateCommand):
"""Create a network for a given tenant."""
resource = 'network'
log = logging.getLogger(__name__ + '.CreateNetwork')
def add_known_arguments(self, parser):
parser.add_argument(
'--admin-state-down',
dest='admin_state', action='store_false',
help='Set Admin State Up to false')
parser.add_argument(
'--admin_state_down',
dest='admin_state', action='store_false',
help=argparse.SUPPRESS)
parser.add_argument(
'--shared',
action='store_true',
help='Set the network as shared')
parser.add_argument(
'name', metavar='NAME',
help='Name of network to create')
def args2body(self, parsed_args):
body = {'network': {
'name': parsed_args.name,
'admin_state_up': parsed_args.admin_state}, }
if parsed_args.tenant_id:
body['network'].update({'tenant_id': parsed_args.tenant_id})
if parsed_args.shared:
body['network'].update({'shared': parsed_args.shared})
return body
class DeleteNetwork(quantumv20.DeleteCommand):
"""Delete a given network."""
log = logging.getLogger(__name__ + '.DeleteNetwork')
resource = 'network'
class UpdateNetwork(quantumv20.UpdateCommand):
"""Update network's information."""
log = logging.getLogger(__name__ + '.UpdateNetwork')
resource = 'network'

View File

@@ -1,89 +0,0 @@
# vim: tabstop=4 shiftwidth=4 softtabstop=4
# Copyright 2013 Nicira Inc.
# 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.
import logging
from quantumclient.quantum import v2_0 as quantumv20
class ListQoSQueue(quantumv20.ListCommand):
"""List queues that belong to a given tenant."""
resource = 'qos_queue'
log = logging.getLogger(__name__ + '.ListQoSQueue')
list_columns = ['id', 'name', 'min', 'max',
'qos_marking', 'dscp', 'default']
class ShowQoSQueue(quantumv20.ShowCommand):
"""Show information of a given queue."""
resource = 'qos_queue'
log = logging.getLogger(__name__ + '.ShowQoSQueue')
allow_names = True
class CreateQoSQueue(quantumv20.CreateCommand):
"""Create a queue."""
resource = 'qos_queue'
log = logging.getLogger(__name__ + '.CreateQoSQueue')
def add_known_arguments(self, parser):
parser.add_argument(
'name', metavar='NAME',
help='Name of queue')
parser.add_argument(
'--min',
help='min-rate'),
parser.add_argument(
'--max',
help='max-rate'),
parser.add_argument(
'--qos-marking',
help='qos marking untrusted/trusted'),
parser.add_argument(
'--default',
default=False,
help=('If true all ports created with be the size of this queue'
' if queue is not specified')),
parser.add_argument(
'--dscp',
help='Differentiated Services Code Point'),
def args2body(self, parsed_args):
params = {'name': parsed_args.name,
'default': parsed_args.default}
if parsed_args.min:
params['min'] = parsed_args.min
if parsed_args.max:
params['max'] = parsed_args.max
if parsed_args.qos_marking:
params['qos_marking'] = parsed_args.qos_marking
if parsed_args.dscp:
params['dscp'] = parsed_args.dscp
if parsed_args.tenant_id:
params['tenant_id'] = parsed_args.tenant_id
return {'qos_queue': params}
class DeleteQoSQueue(quantumv20.DeleteCommand):
"""Delete a given queue."""
log = logging.getLogger(__name__ + '.DeleteQoSQueue')
resource = 'qos_queue'
allow_names = True

View File

@@ -1,159 +0,0 @@
# Copyright 2013 OpenStack LLC.
# 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.
#
# vim: tabstop=4 shiftwidth=4 softtabstop=4
import logging
from quantumclient.common import utils
from quantumclient.openstack.common.gettextutils import _
from quantumclient.quantum import v2_0 as quantumv20
RESOURCE = 'network_gateway'
class ListNetworkGateway(quantumv20.ListCommand):
"""List network gateways for a given tenant."""
resource = RESOURCE
log = logging.getLogger(__name__ + '.ListNetworkGateway')
list_columns = ['id', 'name']
class ShowNetworkGateway(quantumv20.ShowCommand):
"""Show information of a given network gateway."""
resource = RESOURCE
log = logging.getLogger(__name__ + '.ShowNetworkGateway')
class CreateNetworkGateway(quantumv20.CreateCommand):
"""Create a network gateway."""
resource = RESOURCE
log = logging.getLogger(__name__ + '.CreateNetworkGateway')
def add_known_arguments(self, parser):
parser.add_argument(
'name', metavar='NAME',
help='Name of network gateway to create')
parser.add_argument(
'--device',
action='append',
help='device info for this gateway '
'device_id=<device identifier>,'
'interface_name=<name_or_identifier> '
'It can be repeated for multiple devices for HA gateways')
def args2body(self, parsed_args):
body = {self.resource: {
'name': parsed_args.name}}
devices = []
if parsed_args.device:
for device in parsed_args.device:
devices.append(utils.str2dict(device))
if devices:
body[self.resource].update({'devices': devices})
if parsed_args.tenant_id:
body[self.resource].update({'tenant_id': parsed_args.tenant_id})
return body
class DeleteNetworkGateway(quantumv20.DeleteCommand):
"""Delete a given network gateway."""
resource = RESOURCE
log = logging.getLogger(__name__ + '.DeleteNetworkGateway')
class UpdateNetworkGateway(quantumv20.UpdateCommand):
"""Update the name for a network gateway."""
resource = RESOURCE
log = logging.getLogger(__name__ + '.UpdateNetworkGateway')
class NetworkGatewayInterfaceCommand(quantumv20.QuantumCommand):
"""Base class for connecting/disconnecting networks to/from a gateway."""
resource = RESOURCE
def get_parser(self, prog_name):
parser = super(NetworkGatewayInterfaceCommand,
self).get_parser(prog_name)
parser.add_argument(
'net_gateway_id', metavar='NET-GATEWAY-ID',
help='ID of the network gateway')
parser.add_argument(
'network_id', metavar='NETWORK-ID',
help='ID of the internal network to connect on the gateway')
parser.add_argument(
'--segmentation-type',
help=('L2 segmentation strategy on the external side of '
'the gateway (e.g.: VLAN, FLAT)'))
parser.add_argument(
'--segmentation-id',
help=('Identifier for the L2 segment on the external side '
'of the gateway'))
return parser
def retrieve_ids(self, client, args):
gateway_id = quantumv20.find_resourceid_by_name_or_id(
client, self.resource, args.net_gateway_id)
network_id = quantumv20.find_resourceid_by_name_or_id(
client, 'network', args.network_id)
return (gateway_id, network_id)
class ConnectNetworkGateway(NetworkGatewayInterfaceCommand):
"""Add an internal network interface to a router."""
log = logging.getLogger(__name__ + '.ConnectNetworkGateway')
def run(self, parsed_args):
self.log.debug('run(%s)' % parsed_args)
quantum_client = self.get_client()
quantum_client.format = parsed_args.request_format
(gateway_id, network_id) = self.retrieve_ids(quantum_client,
parsed_args)
quantum_client.connect_network_gateway(
gateway_id, {'network_id': network_id,
'segmentation_type': parsed_args.segmentation_type,
'segmentation_id': parsed_args.segmentation_id})
# TODO(Salvatore-Orlando): Do output formatting as
# any other command
print >>self.app.stdout, (
_('Connected network to gateway %s') % gateway_id)
class DisconnectNetworkGateway(NetworkGatewayInterfaceCommand):
"""Remove a network from a network gateway."""
log = logging.getLogger(__name__ + '.DisconnectNetworkGateway')
def run(self, parsed_args):
self.log.debug('run(%s)' % parsed_args)
quantum_client = self.get_client()
quantum_client.format = parsed_args.request_format
(gateway_id, network_id) = self.retrieve_ids(quantum_client,
parsed_args)
quantum_client.disconnect_network_gateway(
gateway_id, {'network_id': network_id,
'segmentation_type': parsed_args.segmentation_type,
'segmentation_id': parsed_args.segmentation_id})
# TODO(Salvatore-Orlando): Do output formatting as
# any other command
print >>self.app.stdout, (
_('Disconnected network from gateway %s') % gateway_id)

View File

@@ -1,4 +1,4 @@
# Copyright 2012 OpenStack LLC.
# Copyright 2013 OpenStack Foundation.
# All Rights Reserved
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
@@ -15,170 +15,4 @@
#
# vim: tabstop=4 shiftwidth=4 softtabstop=4
import argparse
import logging
from quantumclient.common import utils
from quantumclient.quantum import v2_0 as quantumv20
def _format_fixed_ips(port):
try:
return '\n'.join([utils.dumps(ip) for ip in port['fixed_ips']])
except Exception:
return ''
class ListPort(quantumv20.ListCommand):
"""List ports that belong to a given tenant."""
resource = 'port'
log = logging.getLogger(__name__ + '.ListPort')
_formatters = {'fixed_ips': _format_fixed_ips, }
list_columns = ['id', 'name', 'mac_address', 'fixed_ips']
pagination_support = True
sorting_support = True
class ListRouterPort(quantumv20.ListCommand):
"""List ports that belong to a given tenant, with specified router."""
resource = 'port'
log = logging.getLogger(__name__ + '.ListRouterPort')
_formatters = {'fixed_ips': _format_fixed_ips, }
list_columns = ['id', 'name', 'mac_address', 'fixed_ips']
pagination_support = True
sorting_support = True
def get_parser(self, prog_name):
parser = super(ListRouterPort, self).get_parser(prog_name)
parser.add_argument(
'id', metavar='router',
help='ID or name of router to look up')
return parser
def get_data(self, parsed_args):
quantum_client = self.get_client()
quantum_client.format = parsed_args.request_format
_id = quantumv20.find_resourceid_by_name_or_id(
quantum_client, 'router', parsed_args.id)
self.values_specs.append('--device_id=%s' % _id)
return super(ListRouterPort, self).get_data(parsed_args)
class ShowPort(quantumv20.ShowCommand):
"""Show information of a given port."""
resource = 'port'
log = logging.getLogger(__name__ + '.ShowPort')
class CreatePort(quantumv20.CreateCommand):
"""Create a port for a given tenant."""
resource = 'port'
log = logging.getLogger(__name__ + '.CreatePort')
def add_known_arguments(self, parser):
parser.add_argument(
'--name',
help='name of this port')
parser.add_argument(
'--admin-state-down',
dest='admin_state', action='store_false',
help='set admin state up to false')
parser.add_argument(
'--admin_state_down',
dest='admin_state', action='store_false',
help=argparse.SUPPRESS)
parser.add_argument(
'--mac-address',
help='mac address of this port')
parser.add_argument(
'--mac_address',
help=argparse.SUPPRESS)
parser.add_argument(
'--device-id',
help='device id of this port')
parser.add_argument(
'--device_id',
help=argparse.SUPPRESS)
parser.add_argument(
'--fixed-ip', metavar='ip_address=IP_ADDR',
action='append',
help='desired IP for this port: '
'subnet_id=<name_or_id>,ip_address=<ip>, '
'(This option can be repeated.)')
parser.add_argument(
'--fixed_ip',
action='append',
help=argparse.SUPPRESS)
parser.add_argument(
'--security-group', metavar='SECURITY_GROUP',
default=[], action='append', dest='security_groups',
help='security group associated with the port '
'(This option can be repeated)')
parser.add_argument(
'network_id', metavar='NETWORK',
help='Network id or name this port belongs to')
def args2body(self, parsed_args):
_network_id = quantumv20.find_resourceid_by_name_or_id(
self.get_client(), 'network', parsed_args.network_id)
body = {'port': {'admin_state_up': parsed_args.admin_state,
'network_id': _network_id, }, }
if parsed_args.mac_address:
body['port'].update({'mac_address': parsed_args.mac_address})
if parsed_args.device_id:
body['port'].update({'device_id': parsed_args.device_id})
if parsed_args.tenant_id:
body['port'].update({'tenant_id': parsed_args.tenant_id})
if parsed_args.name:
body['port'].update({'name': parsed_args.name})
ips = []
if parsed_args.fixed_ip:
for ip_spec in parsed_args.fixed_ip:
ip_dict = utils.str2dict(ip_spec)
if 'subnet_id' in ip_dict:
subnet_name_id = ip_dict['subnet_id']
_subnet_id = quantumv20.find_resourceid_by_name_or_id(
self.get_client(), 'subnet', subnet_name_id)
ip_dict['subnet_id'] = _subnet_id
ips.append(ip_dict)
if ips:
body['port'].update({'fixed_ips': ips})
_sgids = []
for sg in parsed_args.security_groups:
_sgids.append(quantumv20.find_resourceid_by_name_or_id(
self.get_client(), 'security_group', sg))
if _sgids:
body['port']['security_groups'] = _sgids
return body
class DeletePort(quantumv20.DeleteCommand):
"""Delete a given port."""
resource = 'port'
log = logging.getLogger(__name__ + '.DeletePort')
class UpdatePort(quantumv20.UpdateCommand):
"""Update port's information."""
resource = 'port'
log = logging.getLogger(__name__ + '.UpdatePort')
def add_known_arguments(self, parser):
parser.add_argument(
'--no-security-groups',
action='store_true',
help='remove security groups from port')
def args2body(self, parsed_args):
body = {'port': {}}
if parsed_args.no_security_groups:
body['port'].update({'security_groups': None})
return body
from neutronclient.neutron.v2_0.port import _format_fixed_ips # noqa

View File

@@ -1,232 +0,0 @@
# Copyright 2012 OpenStack LLC.
# 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.
#
# vim: tabstop=4 shiftwidth=4 softtabstop=4
import argparse
import logging
from cliff import lister
from cliff import show
from quantumclient.common import exceptions
from quantumclient.common import utils
from quantumclient.openstack.common.gettextutils import _
from quantumclient.quantum import v2_0 as quantumv20
def get_tenant_id(tenant_id, client):
return (tenant_id if tenant_id else
client.get_quotas_tenant()['tenant']['tenant_id'])
class DeleteQuota(quantumv20.QuantumCommand):
"""Delete defined quotas of a given tenant."""
api = 'network'
resource = 'quota'
log = logging.getLogger(__name__ + '.DeleteQuota')
def get_parser(self, prog_name):
parser = super(DeleteQuota, self).get_parser(prog_name)
parser.add_argument(
'--tenant-id', metavar='tenant-id',
help='the owner tenant ID')
parser.add_argument(
'--tenant_id',
help=argparse.SUPPRESS)
return parser
def run(self, parsed_args):
self.log.debug('run(%s)' % parsed_args)
quantum_client = self.get_client()
quantum_client.format = parsed_args.request_format
tenant_id = get_tenant_id(parsed_args.tenant_id,
quantum_client)
obj_deleter = getattr(quantum_client,
"delete_%s" % self.resource)
obj_deleter(tenant_id)
print >>self.app.stdout, (_('Deleted %(resource)s: %(tenant_id)s')
% {'tenant_id': tenant_id,
'resource': self.resource})
return
class ListQuota(quantumv20.QuantumCommand, lister.Lister):
"""List defined quotas of all tenants."""
api = 'network'
resource = 'quota'
log = logging.getLogger(__name__ + '.ListQuota')
def get_parser(self, prog_name):
parser = super(ListQuota, self).get_parser(prog_name)
return parser
def get_data(self, parsed_args):
self.log.debug('get_data(%s)' % parsed_args)
quantum_client = self.get_client()
search_opts = {}
self.log.debug('search options: %s', search_opts)
quantum_client.format = parsed_args.request_format
obj_lister = getattr(quantum_client,
"list_%ss" % self.resource)
data = obj_lister(**search_opts)
info = []
collection = self.resource + "s"
if collection in data:
info = data[collection]
_columns = len(info) > 0 and sorted(info[0].keys()) or []
return (_columns, (utils.get_item_properties(s, _columns)
for s in info))
class ShowQuota(quantumv20.QuantumCommand, show.ShowOne):
"""Show quotas of a given tenant
"""
api = 'network'
resource = "quota"
log = logging.getLogger(__name__ + '.ShowQuota')
def get_parser(self, prog_name):
parser = super(ShowQuota, self).get_parser(prog_name)
parser.add_argument(
'--tenant-id', metavar='tenant-id',
help='the owner tenant ID')
parser.add_argument(
'--tenant_id',
help=argparse.SUPPRESS)
return parser
def get_data(self, parsed_args):
self.log.debug('get_data(%s)' % parsed_args)
quantum_client = self.get_client()
quantum_client.format = parsed_args.request_format
tenant_id = get_tenant_id(parsed_args.tenant_id,
quantum_client)
params = {}
obj_shower = getattr(quantum_client,
"show_%s" % self.resource)
data = obj_shower(tenant_id, **params)
if self.resource in data:
for k, v in data[self.resource].iteritems():
if isinstance(v, list):
value = ""
for _item in v:
if value:
value += "\n"
if isinstance(_item, dict):
value += utils.dumps(_item)
else:
value += str(_item)
data[self.resource][k] = value
elif v is None:
data[self.resource][k] = ''
return zip(*sorted(data[self.resource].iteritems()))
else:
return None
class UpdateQuota(quantumv20.QuantumCommand, show.ShowOne):
"""Define tenant's quotas not to use defaults."""
resource = 'quota'
log = logging.getLogger(__name__ + '.UpdateQuota')
def get_parser(self, prog_name):
parser = super(UpdateQuota, self).get_parser(prog_name)
parser.add_argument(
'--tenant-id', metavar='tenant-id',
help='the owner tenant ID')
parser.add_argument(
'--tenant_id',
help=argparse.SUPPRESS)
parser.add_argument(
'--network', metavar='networks',
help='the limit of networks')
parser.add_argument(
'--subnet', metavar='subnets',
help='the limit of subnets')
parser.add_argument(
'--port', metavar='ports',
help='the limit of ports')
parser.add_argument(
'--router', metavar='routers',
help='the limit of routers')
parser.add_argument(
'--floatingip', metavar='floatingips',
help='the limit of floating IPs')
parser.add_argument(
'--security-group', metavar='security_groups',
help='the limit of security groups')
parser.add_argument(
'--security-group-rule', metavar='security_group_rules',
help='the limit of security groups rules')
return parser
def _validate_int(self, name, value):
try:
return_value = int(value)
except Exception:
message = (_('quota limit for %(name)s must be an integer') %
{'name': name})
raise exceptions.QuantumClientException(message=message)
return return_value
def args2body(self, parsed_args):
quota = {}
for resource in ('network', 'subnet', 'port', 'router', 'floatingip',
'security_group', 'security_group_rule'):
if getattr(parsed_args, resource):
quota[resource] = self._validate_int(
resource,
getattr(parsed_args, resource))
return {self.resource: quota}
def get_data(self, parsed_args):
self.log.debug('run(%s)' % parsed_args)
quantum_client = self.get_client()
quantum_client.format = parsed_args.request_format
_extra_values = quantumv20.parse_args_to_dict(self.values_specs)
quantumv20._merge_args(self, parsed_args, _extra_values,
self.values_specs)
body = self.args2body(parsed_args)
if self.resource in body:
body[self.resource].update(_extra_values)
else:
body[self.resource] = _extra_values
obj_updator = getattr(quantum_client,
"update_%s" % self.resource)
tenant_id = get_tenant_id(parsed_args.tenant_id,
quantum_client)
data = obj_updator(tenant_id, body)
if self.resource in data:
for k, v in data[self.resource].iteritems():
if isinstance(v, list):
value = ""
for _item in v:
if value:
value += "\n"
if isinstance(_item, dict):
value += utils.dumps(_item)
else:
value += str(_item)
data[self.resource][k] = value
elif v is None:
data[self.resource][k] = ''
return zip(*sorted(data[self.resource].iteritems()))
else:
return None

View File

@@ -1,230 +0,0 @@
# Copyright 2012 OpenStack LLC.
# 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.
#
# vim: tabstop=4 shiftwidth=4 softtabstop=4
import argparse
import logging
from quantumclient.common import exceptions
from quantumclient.common import utils
from quantumclient.openstack.common.gettextutils import _
from quantumclient.quantum import v2_0 as quantumv20
def _format_external_gateway_info(router):
try:
return utils.dumps(router['external_gateway_info'])
except Exception:
return ''
class ListRouter(quantumv20.ListCommand):
"""List routers that belong to a given tenant."""
resource = 'router'
log = logging.getLogger(__name__ + '.ListRouter')
_formatters = {'external_gateway_info': _format_external_gateway_info, }
list_columns = ['id', 'name', 'external_gateway_info']
pagination_support = True
sorting_support = True
class ShowRouter(quantumv20.ShowCommand):
"""Show information of a given router."""
resource = 'router'
log = logging.getLogger(__name__ + '.ShowRouter')
class CreateRouter(quantumv20.CreateCommand):
"""Create a router for a given tenant."""
resource = 'router'
log = logging.getLogger(__name__ + '.CreateRouter')
_formatters = {'external_gateway_info': _format_external_gateway_info, }
def add_known_arguments(self, parser):
parser.add_argument(
'--admin-state-down',
dest='admin_state', action='store_false',
help='Set Admin State Up to false')
parser.add_argument(
'--admin_state_down',
dest='admin_state', action='store_false',
help=argparse.SUPPRESS)
parser.add_argument(
'name', metavar='NAME',
help='Name of router to create')
def args2body(self, parsed_args):
body = {'router': {
'name': parsed_args.name,
'admin_state_up': parsed_args.admin_state, }, }
if parsed_args.tenant_id:
body['router'].update({'tenant_id': parsed_args.tenant_id})
return body
class DeleteRouter(quantumv20.DeleteCommand):
"""Delete a given router."""
log = logging.getLogger(__name__ + '.DeleteRouter')
resource = 'router'
class UpdateRouter(quantumv20.UpdateCommand):
"""Update router's information."""
log = logging.getLogger(__name__ + '.UpdateRouter')
resource = 'router'
class RouterInterfaceCommand(quantumv20.QuantumCommand):
"""Based class to Add/Remove router interface."""
api = 'network'
resource = 'router'
def call_api(self, quantum_client, router_id, body):
raise NotImplementedError()
def success_message(self, router_id, portinfo):
raise NotImplementedError()
def get_parser(self, prog_name):
parser = super(RouterInterfaceCommand, self).get_parser(prog_name)
parser.add_argument(
'router_id', metavar='router-id',
help='ID of the router')
parser.add_argument(
'interface', metavar='INTERFACE',
help='The format is "SUBNET|subnet=SUBNET|port=PORT". '
'Either a subnet or port must be specified. '
'Both ID and name are accepted as SUBNET or PORT. '
'Note that "subnet=" can be omitted when specifying subnet.')
return parser
def run(self, parsed_args):
self.log.debug('run(%s)' % parsed_args)
quantum_client = self.get_client()
quantum_client.format = parsed_args.request_format
if '=' in parsed_args.interface:
resource, value = parsed_args.interface.split('=', 1)
if resource not in ['subnet', 'port']:
exceptions.CommandError('You must specify either subnet or '
'port for INTERFACE parameter.')
else:
resource = 'subnet'
value = parsed_args.interface
_router_id = quantumv20.find_resourceid_by_name_or_id(
quantum_client, self.resource, parsed_args.router_id)
_interface_id = quantumv20.find_resourceid_by_name_or_id(
quantum_client, resource, value)
body = {'%s_id' % resource: _interface_id}
portinfo = self.call_api(quantum_client, _router_id, body)
print >>self.app.stdout, self.success_message(parsed_args.router_id,
portinfo)
class AddInterfaceRouter(RouterInterfaceCommand):
"""Add an internal network interface to a router."""
log = logging.getLogger(__name__ + '.AddInterfaceRouter')
def call_api(self, quantum_client, router_id, body):
return quantum_client.add_interface_router(router_id, body)
def success_message(self, router_id, portinfo):
return (_('Added interface %(port)s to router %(router)s.') %
{'router': router_id, 'port': portinfo['port_id']})
class RemoveInterfaceRouter(RouterInterfaceCommand):
"""Remove an internal network interface from a router."""
log = logging.getLogger(__name__ + '.RemoveInterfaceRouter')
def call_api(self, quantum_client, router_id, body):
return quantum_client.remove_interface_router(router_id, body)
def success_message(self, router_id, portinfo):
# portinfo is not used since it is None for router-interface-delete.
return _('Removed interface from router %s.') % router_id
class SetGatewayRouter(quantumv20.QuantumCommand):
"""Set the external network gateway for a router."""
log = logging.getLogger(__name__ + '.SetGatewayRouter')
api = 'network'
resource = 'router'
def get_parser(self, prog_name):
parser = super(SetGatewayRouter, self).get_parser(prog_name)
parser.add_argument(
'router_id', metavar='router-id',
help='ID of the router')
parser.add_argument(
'external_network_id', metavar='external-network-id',
help='ID of the external network for the gateway')
parser.add_argument(
'--disable-snat', action='store_false', dest='enable_snat',
help='Disable Source NAT on the router gateway')
return parser
def run(self, parsed_args):
self.log.debug('run(%s)' % parsed_args)
quantum_client = self.get_client()
quantum_client.format = parsed_args.request_format
_router_id = quantumv20.find_resourceid_by_name_or_id(
quantum_client, self.resource, parsed_args.router_id)
_ext_net_id = quantumv20.find_resourceid_by_name_or_id(
quantum_client, 'network', parsed_args.external_network_id)
quantum_client.add_gateway_router(
_router_id,
{'network_id': _ext_net_id,
'enable_snat': parsed_args.enable_snat})
print >>self.app.stdout, (
_('Set gateway for router %s') % parsed_args.router_id)
class RemoveGatewayRouter(quantumv20.QuantumCommand):
"""Remove an external network gateway from a router."""
log = logging.getLogger(__name__ + '.RemoveGatewayRouter')
api = 'network'
resource = 'router'
def get_parser(self, prog_name):
parser = super(RemoveGatewayRouter, self).get_parser(prog_name)
parser.add_argument(
'router_id', metavar='router-id',
help='ID of the router')
return parser
def run(self, parsed_args):
self.log.debug('run(%s)' % parsed_args)
quantum_client = self.get_client()
quantum_client.format = parsed_args.request_format
_router_id = quantumv20.find_resourceid_by_name_or_id(
quantum_client, self.resource, parsed_args.router_id)
quantum_client.remove_gateway_router(_router_id)
print >>self.app.stdout, (
_('Removed gateway from router %s') % parsed_args.router_id)

View File

@@ -1,259 +0,0 @@
# Copyright 2012 OpenStack LLC.
# 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.
#
# vim: tabstop=4 shiftwidth=4 softtabstop=4
import argparse
import logging
from quantumclient.quantum import v2_0 as quantumv20
class ListSecurityGroup(quantumv20.ListCommand):
"""List security groups that belong to a given tenant."""
resource = 'security_group'
log = logging.getLogger(__name__ + '.ListSecurityGroup')
list_columns = ['id', 'name', 'description']
pagination_support = True
sorting_support = True
class ShowSecurityGroup(quantumv20.ShowCommand):
"""Show information of a given security group."""
resource = 'security_group'
log = logging.getLogger(__name__ + '.ShowSecurityGroup')
allow_names = True
class CreateSecurityGroup(quantumv20.CreateCommand):
"""Create a security group."""
resource = 'security_group'
log = logging.getLogger(__name__ + '.CreateSecurityGroup')
def add_known_arguments(self, parser):
parser.add_argument(
'name', metavar='NAME',
help='Name of security group')
parser.add_argument(
'--description',
help='description of security group')
def args2body(self, parsed_args):
body = {'security_group': {
'name': parsed_args.name}}
if parsed_args.description:
body['security_group'].update(
{'description': parsed_args.description})
if parsed_args.tenant_id:
body['security_group'].update({'tenant_id': parsed_args.tenant_id})
return body
class DeleteSecurityGroup(quantumv20.DeleteCommand):
"""Delete a given security group."""
log = logging.getLogger(__name__ + '.DeleteSecurityGroup')
resource = 'security_group'
allow_names = True
class UpdateSecurityGroup(quantumv20.UpdateCommand):
"""Update a given security group."""
log = logging.getLogger(__name__ + '.UpdateSecurityGroup')
resource = 'security_group'
def add_known_arguments(self, parser):
parser.add_argument(
'--name',
help='Name of security group')
parser.add_argument(
'--description',
help='description of security group')
def args2body(self, parsed_args):
body = {'security_group': {}}
if parsed_args.name:
body['security_group'].update(
{'name': parsed_args.name})
if parsed_args.description:
body['security_group'].update(
{'description': parsed_args.description})
return body
class ListSecurityGroupRule(quantumv20.ListCommand):
"""List security group rules that belong to a given tenant."""
resource = 'security_group_rule'
log = logging.getLogger(__name__ + '.ListSecurityGroupRule')
list_columns = ['id', 'security_group_id', 'direction', 'protocol',
'remote_ip_prefix', 'remote_group_id']
replace_rules = {'security_group_id': 'security_group',
'remote_group_id': 'remote_group'}
pagination_support = True
sorting_support = True
def get_parser(self, prog_name):
parser = super(ListSecurityGroupRule, self).get_parser(prog_name)
parser.add_argument(
'--no-nameconv', action='store_true',
help='Do not convert security group ID to its name')
return parser
@staticmethod
def replace_columns(cols, rules, reverse=False):
if reverse:
rules = dict((rules[k], k) for k in rules.keys())
return [rules.get(col, col) for col in cols]
def retrieve_list(self, parsed_args):
parsed_args.fields = self.replace_columns(parsed_args.fields,
self.replace_rules,
reverse=True)
return super(ListSecurityGroupRule, self).retrieve_list(parsed_args)
def extend_list(self, data, parsed_args):
if parsed_args.no_nameconv:
return
quantum_client = self.get_client()
search_opts = {'fields': ['id', 'name']}
if self.pagination_support:
page_size = parsed_args.page_size
if page_size:
search_opts.update({'limit': page_size})
sec_group_ids = set()
for rule in data:
for key in self.replace_rules:
sec_group_ids.add(rule[key])
search_opts.update({"id": sec_group_ids})
secgroups = quantum_client.list_security_groups(**search_opts)
secgroups = secgroups.get('security_groups', [])
sg_dict = dict([(sg['id'], sg['name'])
for sg in secgroups if sg['name']])
for rule in data:
for key in self.replace_rules:
rule[key] = sg_dict.get(rule[key], rule[key])
def setup_columns(self, info, parsed_args):
parsed_args.columns = self.replace_columns(parsed_args.columns,
self.replace_rules,
reverse=True)
# NOTE(amotoki): 2nd element of the tuple returned by setup_columns()
# is a generator, so if you need to create a look using the generator
# object, you need to recreate a generator to show a list expectedly.
info = super(ListSecurityGroupRule, self).setup_columns(info,
parsed_args)
cols = info[0]
if not parsed_args.no_nameconv:
cols = self.replace_columns(info[0], self.replace_rules)
parsed_args.columns = cols
return (cols, info[1])
class ShowSecurityGroupRule(quantumv20.ShowCommand):
"""Show information of a given security group rule."""
resource = 'security_group_rule'
log = logging.getLogger(__name__ + '.ShowSecurityGroupRule')
allow_names = False
class CreateSecurityGroupRule(quantumv20.CreateCommand):
"""Create a security group rule."""
resource = 'security_group_rule'
log = logging.getLogger(__name__ + '.CreateSecurityGroupRule')
def add_known_arguments(self, parser):
parser.add_argument(
'security_group_id', metavar='SECURITY_GROUP',
help='Security group name or id to add rule.')
parser.add_argument(
'--direction',
default='ingress', choices=['ingress', 'egress'],
help='direction of traffic: ingress/egress')
parser.add_argument(
'--ethertype',
default='IPv4',
help='IPv4/IPv6')
parser.add_argument(
'--protocol',
help='protocol of packet')
parser.add_argument(
'--port-range-min',
help='starting port range')
parser.add_argument(
'--port_range_min',
help=argparse.SUPPRESS)
parser.add_argument(
'--port-range-max',
help='ending port range')
parser.add_argument(
'--port_range_max',
help=argparse.SUPPRESS)
parser.add_argument(
'--remote-ip-prefix',
help='cidr to match on')
parser.add_argument(
'--remote_ip_prefix',
help=argparse.SUPPRESS)
parser.add_argument(
'--remote-group-id', metavar='REMOTE_GROUP',
help='remote security group name or id to apply rule')
parser.add_argument(
'--remote_group_id',
help=argparse.SUPPRESS)
def args2body(self, parsed_args):
_security_group_id = quantumv20.find_resourceid_by_name_or_id(
self.get_client(), 'security_group', parsed_args.security_group_id)
body = {'security_group_rule': {
'security_group_id': _security_group_id,
'direction': parsed_args.direction,
'ethertype': parsed_args.ethertype}}
if parsed_args.protocol:
body['security_group_rule'].update(
{'protocol': parsed_args.protocol})
if parsed_args.port_range_min:
body['security_group_rule'].update(
{'port_range_min': parsed_args.port_range_min})
if parsed_args.port_range_max:
body['security_group_rule'].update(
{'port_range_max': parsed_args.port_range_max})
if parsed_args.remote_ip_prefix:
body['security_group_rule'].update(
{'remote_ip_prefix': parsed_args.remote_ip_prefix})
if parsed_args.remote_group_id:
_remote_group_id = quantumv20.find_resourceid_by_name_or_id(
self.get_client(), 'security_group',
parsed_args.remote_group_id)
body['security_group_rule'].update(
{'remote_group_id': _remote_group_id})
if parsed_args.tenant_id:
body['security_group_rule'].update(
{'tenant_id': parsed_args.tenant_id})
return body
class DeleteSecurityGroupRule(quantumv20.DeleteCommand):
"""Delete a given security group rule."""
log = logging.getLogger(__name__ + '.DeleteSecurityGroupRule')
resource = 'security_group_rule'
allow_names = False

View File

@@ -1,168 +0,0 @@
# Copyright 2012 OpenStack LLC.
# 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.
#
# vim: tabstop=4 shiftwidth=4 softtabstop=4
import argparse
import logging
from quantumclient.common import exceptions
from quantumclient.common import utils
from quantumclient.quantum import v2_0 as quantumv20
def _format_allocation_pools(subnet):
try:
return '\n'.join([utils.dumps(pool) for pool in
subnet['allocation_pools']])
except Exception:
return ''
def _format_dns_nameservers(subnet):
try:
return '\n'.join([utils.dumps(server) for server in
subnet['dns_nameservers']])
except Exception:
return ''
def _format_host_routes(subnet):
try:
return '\n'.join([utils.dumps(route) for route in
subnet['host_routes']])
except Exception:
return ''
class ListSubnet(quantumv20.ListCommand):
"""List networks that belong to a given tenant."""
resource = 'subnet'
log = logging.getLogger(__name__ + '.ListSubnet')
_formatters = {'allocation_pools': _format_allocation_pools,
'dns_nameservers': _format_dns_nameservers,
'host_routes': _format_host_routes, }
list_columns = ['id', 'name', 'cidr', 'allocation_pools']
pagination_support = True
sorting_support = True
class ShowSubnet(quantumv20.ShowCommand):
"""Show information of a given subnet."""
resource = 'subnet'
log = logging.getLogger(__name__ + '.ShowSubnet')
class CreateSubnet(quantumv20.CreateCommand):
"""Create a subnet for a given tenant."""
resource = 'subnet'
log = logging.getLogger(__name__ + '.CreateSubnet')
def add_known_arguments(self, parser):
parser.add_argument(
'--name',
help='name of this subnet')
parser.add_argument(
'--ip-version',
type=int,
default=4, choices=[4, 6],
help='IP version with default 4')
parser.add_argument(
'--ip_version',
type=int,
choices=[4, 6],
help=argparse.SUPPRESS)
parser.add_argument(
'--gateway', metavar='GATEWAY_IP',
help='gateway ip of this subnet')
parser.add_argument(
'--no-gateway',
action='store_true',
help='No distribution of gateway')
parser.add_argument(
'--allocation-pool', metavar='start=IP_ADDR,end=IP_ADDR',
action='append', dest='allocation_pools', type=utils.str2dict,
help='Allocation pool IP addresses for this subnet '
'(This option can be repeated)')
parser.add_argument(
'--allocation_pool',
action='append', dest='allocation_pools', type=utils.str2dict,
help=argparse.SUPPRESS)
parser.add_argument(
'--host-route', metavar='destination=CIDR,nexthop=IP_ADDR',
action='append', dest='host_routes', type=utils.str2dict,
help='Additional route (This option can be repeated)')
parser.add_argument(
'--dns-nameserver', metavar='DNS_NAMESERVER',
action='append', dest='dns_nameservers',
help='DNS name server for this subnet '
'(This option can be repeated)')
parser.add_argument(
'--disable-dhcp',
action='store_true',
help='Disable DHCP for this subnet')
parser.add_argument(
'network_id', metavar='NETWORK',
help='network id or name this subnet belongs to')
parser.add_argument(
'cidr', metavar='CIDR',
help='cidr of subnet to create')
def args2body(self, parsed_args):
_network_id = quantumv20.find_resourceid_by_name_or_id(
self.get_client(), 'network', parsed_args.network_id)
body = {'subnet': {'cidr': parsed_args.cidr,
'network_id': _network_id,
'ip_version': parsed_args.ip_version, }, }
if parsed_args.gateway and parsed_args.no_gateway:
raise exceptions.CommandError("--gateway option and "
"--no-gateway option can "
"not be used same time")
if parsed_args.no_gateway:
body['subnet'].update({'gateway_ip': None})
if parsed_args.gateway:
body['subnet'].update({'gateway_ip': parsed_args.gateway})
if parsed_args.tenant_id:
body['subnet'].update({'tenant_id': parsed_args.tenant_id})
if parsed_args.name:
body['subnet'].update({'name': parsed_args.name})
if parsed_args.disable_dhcp:
body['subnet'].update({'enable_dhcp': False})
if parsed_args.allocation_pools:
body['subnet']['allocation_pools'] = parsed_args.allocation_pools
if parsed_args.host_routes:
body['subnet']['host_routes'] = parsed_args.host_routes
if parsed_args.dns_nameservers:
body['subnet']['dns_nameservers'] = parsed_args.dns_nameservers
return body
class DeleteSubnet(quantumv20.DeleteCommand):
"""Delete a given subnet."""
resource = 'subnet'
log = logging.getLogger(__name__ + '.DeleteSubnet')
class UpdateSubnet(quantumv20.UpdateCommand):
"""Update subnet's information."""
resource = 'subnet'
log = logging.getLogger(__name__ + '.UpdateSubnet')

View File

@@ -19,637 +19,14 @@
Command-line interface to the Quantum APIs
"""
import argparse
import logging
import os
import sys
from cliff import app
from cliff import commandmanager
from quantumclient.common import clientmanager
from quantumclient.common import exceptions as exc
from quantumclient.common import utils
from quantumclient.openstack.common import strutils
from quantumclient.version import __version__
VERSION = '2.0'
QUANTUM_API_VERSION = '2.0'
def run_command(cmd, cmd_parser, sub_argv):
_argv = sub_argv
index = -1
values_specs = []
if '--' in sub_argv:
index = sub_argv.index('--')
_argv = sub_argv[:index]
values_specs = sub_argv[index:]
known_args, _values_specs = cmd_parser.parse_known_args(_argv)
cmd.values_specs = (index == -1 and _values_specs or values_specs)
return cmd.run(known_args)
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', '')
COMMAND_V2 = {
'net-list': utils.import_class(
'quantumclient.quantum.v2_0.network.ListNetwork'),
'net-external-list': utils.import_class(
'quantumclient.quantum.v2_0.network.ListExternalNetwork'),
'net-show': utils.import_class(
'quantumclient.quantum.v2_0.network.ShowNetwork'),
'net-create': utils.import_class(
'quantumclient.quantum.v2_0.network.CreateNetwork'),
'net-delete': utils.import_class(
'quantumclient.quantum.v2_0.network.DeleteNetwork'),
'net-update': utils.import_class(
'quantumclient.quantum.v2_0.network.UpdateNetwork'),
'subnet-list': utils.import_class(
'quantumclient.quantum.v2_0.subnet.ListSubnet'),
'subnet-show': utils.import_class(
'quantumclient.quantum.v2_0.subnet.ShowSubnet'),
'subnet-create': utils.import_class(
'quantumclient.quantum.v2_0.subnet.CreateSubnet'),
'subnet-delete': utils.import_class(
'quantumclient.quantum.v2_0.subnet.DeleteSubnet'),
'subnet-update': utils.import_class(
'quantumclient.quantum.v2_0.subnet.UpdateSubnet'),
'port-list': utils.import_class(
'quantumclient.quantum.v2_0.port.ListPort'),
'port-show': utils.import_class(
'quantumclient.quantum.v2_0.port.ShowPort'),
'port-create': utils.import_class(
'quantumclient.quantum.v2_0.port.CreatePort'),
'port-delete': utils.import_class(
'quantumclient.quantum.v2_0.port.DeletePort'),
'port-update': utils.import_class(
'quantumclient.quantum.v2_0.port.UpdatePort'),
'quota-list': utils.import_class(
'quantumclient.quantum.v2_0.quota.ListQuota'),
'quota-show': utils.import_class(
'quantumclient.quantum.v2_0.quota.ShowQuota'),
'quota-delete': utils.import_class(
'quantumclient.quantum.v2_0.quota.DeleteQuota'),
'quota-update': utils.import_class(
'quantumclient.quantum.v2_0.quota.UpdateQuota'),
'ext-list': utils.import_class(
'quantumclient.quantum.v2_0.extension.ListExt'),
'ext-show': utils.import_class(
'quantumclient.quantum.v2_0.extension.ShowExt'),
'router-list': utils.import_class(
'quantumclient.quantum.v2_0.router.ListRouter'),
'router-port-list': utils.import_class(
'quantumclient.quantum.v2_0.port.ListRouterPort'),
'router-show': utils.import_class(
'quantumclient.quantum.v2_0.router.ShowRouter'),
'router-create': utils.import_class(
'quantumclient.quantum.v2_0.router.CreateRouter'),
'router-delete': utils.import_class(
'quantumclient.quantum.v2_0.router.DeleteRouter'),
'router-update': utils.import_class(
'quantumclient.quantum.v2_0.router.UpdateRouter'),
'router-interface-add': utils.import_class(
'quantumclient.quantum.v2_0.router.AddInterfaceRouter'),
'router-interface-delete': utils.import_class(
'quantumclient.quantum.v2_0.router.RemoveInterfaceRouter'),
'router-gateway-set': utils.import_class(
'quantumclient.quantum.v2_0.router.SetGatewayRouter'),
'router-gateway-clear': utils.import_class(
'quantumclient.quantum.v2_0.router.RemoveGatewayRouter'),
'floatingip-list': utils.import_class(
'quantumclient.quantum.v2_0.floatingip.ListFloatingIP'),
'floatingip-show': utils.import_class(
'quantumclient.quantum.v2_0.floatingip.ShowFloatingIP'),
'floatingip-create': utils.import_class(
'quantumclient.quantum.v2_0.floatingip.CreateFloatingIP'),
'floatingip-delete': utils.import_class(
'quantumclient.quantum.v2_0.floatingip.DeleteFloatingIP'),
'floatingip-associate': utils.import_class(
'quantumclient.quantum.v2_0.floatingip.AssociateFloatingIP'),
'floatingip-disassociate': utils.import_class(
'quantumclient.quantum.v2_0.floatingip.DisassociateFloatingIP'),
'security-group-list': utils.import_class(
'quantumclient.quantum.v2_0.securitygroup.ListSecurityGroup'),
'security-group-show': utils.import_class(
'quantumclient.quantum.v2_0.securitygroup.ShowSecurityGroup'),
'security-group-create': utils.import_class(
'quantumclient.quantum.v2_0.securitygroup.CreateSecurityGroup'),
'security-group-delete': utils.import_class(
'quantumclient.quantum.v2_0.securitygroup.DeleteSecurityGroup'),
'security-group-update': utils.import_class(
'quantumclient.quantum.v2_0.securitygroup.UpdateSecurityGroup'),
'security-group-rule-list': utils.import_class(
'quantumclient.quantum.v2_0.securitygroup.ListSecurityGroupRule'),
'security-group-rule-show': utils.import_class(
'quantumclient.quantum.v2_0.securitygroup.ShowSecurityGroupRule'),
'security-group-rule-create': utils.import_class(
'quantumclient.quantum.v2_0.securitygroup.CreateSecurityGroupRule'),
'security-group-rule-delete': utils.import_class(
'quantumclient.quantum.v2_0.securitygroup.DeleteSecurityGroupRule'),
'lb-vip-list': utils.import_class(
'quantumclient.quantum.v2_0.lb.vip.ListVip'),
'lb-vip-show': utils.import_class(
'quantumclient.quantum.v2_0.lb.vip.ShowVip'),
'lb-vip-create': utils.import_class(
'quantumclient.quantum.v2_0.lb.vip.CreateVip'),
'lb-vip-update': utils.import_class(
'quantumclient.quantum.v2_0.lb.vip.UpdateVip'),
'lb-vip-delete': utils.import_class(
'quantumclient.quantum.v2_0.lb.vip.DeleteVip'),
'lb-pool-list': utils.import_class(
'quantumclient.quantum.v2_0.lb.pool.ListPool'),
'lb-pool-show': utils.import_class(
'quantumclient.quantum.v2_0.lb.pool.ShowPool'),
'lb-pool-create': utils.import_class(
'quantumclient.quantum.v2_0.lb.pool.CreatePool'),
'lb-pool-update': utils.import_class(
'quantumclient.quantum.v2_0.lb.pool.UpdatePool'),
'lb-pool-delete': utils.import_class(
'quantumclient.quantum.v2_0.lb.pool.DeletePool'),
'lb-pool-stats': utils.import_class(
'quantumclient.quantum.v2_0.lb.pool.RetrievePoolStats'),
'lb-member-list': utils.import_class(
'quantumclient.quantum.v2_0.lb.member.ListMember'),
'lb-member-show': utils.import_class(
'quantumclient.quantum.v2_0.lb.member.ShowMember'),
'lb-member-create': utils.import_class(
'quantumclient.quantum.v2_0.lb.member.CreateMember'),
'lb-member-update': utils.import_class(
'quantumclient.quantum.v2_0.lb.member.UpdateMember'),
'lb-member-delete': utils.import_class(
'quantumclient.quantum.v2_0.lb.member.DeleteMember'),
'lb-healthmonitor-list': utils.import_class(
'quantumclient.quantum.v2_0.lb.healthmonitor.ListHealthMonitor'),
'lb-healthmonitor-show': utils.import_class(
'quantumclient.quantum.v2_0.lb.healthmonitor.ShowHealthMonitor'),
'lb-healthmonitor-create': utils.import_class(
'quantumclient.quantum.v2_0.lb.healthmonitor.CreateHealthMonitor'),
'lb-healthmonitor-update': utils.import_class(
'quantumclient.quantum.v2_0.lb.healthmonitor.UpdateHealthMonitor'),
'lb-healthmonitor-delete': utils.import_class(
'quantumclient.quantum.v2_0.lb.healthmonitor.DeleteHealthMonitor'),
'lb-healthmonitor-associate': utils.import_class(
'quantumclient.quantum.v2_0.lb.healthmonitor.AssociateHealthMonitor'),
'lb-healthmonitor-disassociate': utils.import_class(
'quantumclient.quantum.v2_0.lb.healthmonitor'
'.DisassociateHealthMonitor'),
'queue-create': utils.import_class(
'quantumclient.quantum.v2_0.nvp_qos_queue.CreateQoSQueue'),
'queue-delete': utils.import_class(
'quantumclient.quantum.v2_0.nvp_qos_queue.DeleteQoSQueue'),
'queue-show': utils.import_class(
'quantumclient.quantum.v2_0.nvp_qos_queue.ShowQoSQueue'),
'queue-list': utils.import_class(
'quantumclient.quantum.v2_0.nvp_qos_queue.ListQoSQueue'),
'agent-list': utils.import_class(
'quantumclient.quantum.v2_0.agent.ListAgent'),
'agent-show': utils.import_class(
'quantumclient.quantum.v2_0.agent.ShowAgent'),
'agent-delete': utils.import_class(
'quantumclient.quantum.v2_0.agent.DeleteAgent'),
'agent-update': utils.import_class(
'quantumclient.quantum.v2_0.agent.UpdateAgent'),
'net-gateway-create': utils.import_class(
'quantumclient.quantum.v2_0.nvpnetworkgateway.CreateNetworkGateway'),
'net-gateway-update': utils.import_class(
'quantumclient.quantum.v2_0.nvpnetworkgateway.UpdateNetworkGateway'),
'net-gateway-delete': utils.import_class(
'quantumclient.quantum.v2_0.nvpnetworkgateway.DeleteNetworkGateway'),
'net-gateway-show': utils.import_class(
'quantumclient.quantum.v2_0.nvpnetworkgateway.ShowNetworkGateway'),
'net-gateway-list': utils.import_class(
'quantumclient.quantum.v2_0.nvpnetworkgateway.ListNetworkGateway'),
'net-gateway-connect': utils.import_class(
'quantumclient.quantum.v2_0.nvpnetworkgateway.ConnectNetworkGateway'),
'net-gateway-disconnect': utils.import_class(
'quantumclient.quantum.v2_0.nvpnetworkgateway.'
'DisconnectNetworkGateway'),
'dhcp-agent-network-add': utils.import_class(
'quantumclient.quantum.v2_0.agentscheduler.AddNetworkToDhcpAgent'),
'dhcp-agent-network-remove': utils.import_class(
'quantumclient.quantum.v2_0.agentscheduler.'
'RemoveNetworkFromDhcpAgent'),
'net-list-on-dhcp-agent': utils.import_class(
'quantumclient.quantum.v2_0.agentscheduler.'
'ListNetworksOnDhcpAgent'),
'dhcp-agent-list-hosting-net': utils.import_class(
'quantumclient.quantum.v2_0.agentscheduler.'
'ListDhcpAgentsHostingNetwork'),
'l3-agent-router-add': utils.import_class(
'quantumclient.quantum.v2_0.agentscheduler.AddRouterToL3Agent'),
'l3-agent-router-remove': utils.import_class(
'quantumclient.quantum.v2_0.agentscheduler.RemoveRouterFromL3Agent'),
'router-list-on-l3-agent': utils.import_class(
'quantumclient.quantum.v2_0.agentscheduler.ListRoutersOnL3Agent'),
'l3-agent-list-hosting-router': utils.import_class(
'quantumclient.quantum.v2_0.agentscheduler.ListL3AgentsHostingRouter'),
}
COMMANDS = {'2.0': COMMAND_V2}
class HelpAction(argparse.Action):
"""Provide a custom action so the -h and --help options
to the main app will print a list of the commands.
The commands are determined by checking the CommandManager
instance, passed in as the "default" value for the action.
"""
def __call__(self, parser, namespace, values, option_string=None):
outputs = []
max_len = 0
app = self.default
parser.print_help(app.stdout)
app.stdout.write('\nCommands for API v%s:\n' % app.api_version)
command_manager = app.command_manager
for name, ep in sorted(command_manager):
factory = ep.load()
cmd = factory(self, None)
one_liner = cmd.get_description().split('\n')[0]
outputs.append((name, one_liner))
max_len = max(len(name), max_len)
for (name, one_liner) in outputs:
app.stdout.write(' %s %s\n' % (name.ljust(max_len), one_liner))
sys.exit(0)
class QuantumShell(app.App):
CONSOLE_MESSAGE_FORMAT = '%(message)s'
DEBUG_MESSAGE_FORMAT = '%(levelname)s: %(name)s %(message)s'
log = logging.getLogger(__name__)
def __init__(self, apiversion):
super(QuantumShell, self).__init__(
description=__doc__.strip(),
version=VERSION,
command_manager=commandmanager.CommandManager('quantum.cli'), )
self.commands = COMMANDS
for k, v in self.commands[apiversion].items():
self.command_manager.add_command(k, v)
# This is instantiated in initialize_app() only when using
# password flow auth
self.auth_client = None
self.api_version = apiversion
def build_option_parser(self, description, version):
"""Return an argparse option parser for this application.
Subclasses may override this method to extend
the parser with more global options.
:param description: full description of the application
:paramtype description: str
:param version: version number for the application
:paramtype version: str
"""
parser = argparse.ArgumentParser(
description=description,
add_help=False, )
parser.add_argument(
'--version',
action='version',
version=__version__, )
parser.add_argument(
'-v', '--verbose',
action='count',
dest='verbose_level',
default=self.DEFAULT_VERBOSE_LEVEL,
help='Increase verbosity of output. Can be repeated.', )
parser.add_argument(
'-q', '--quiet',
action='store_const',
dest='verbose_level',
const=0,
help='suppress output except warnings and errors', )
parser.add_argument(
'-h', '--help',
action=HelpAction,
nargs=0,
default=self, # tricky
help="show this help message and exit", )
parser.add_argument(
'--debug',
default=False,
action='store_true',
help='show tracebacks on errors', )
# Global arguments
parser.add_argument(
'--os-auth-strategy', metavar='<auth-strategy>',
default=env('OS_AUTH_STRATEGY', default='keystone'),
help='Authentication strategy (Env: OS_AUTH_STRATEGY'
', default keystone). For now, any other value will'
' disable the authentication')
parser.add_argument(
'--os_auth_strategy',
help=argparse.SUPPRESS)
parser.add_argument(
'--os-auth-url', metavar='<auth-url>',
default=env('OS_AUTH_URL'),
help='Authentication URL (Env: OS_AUTH_URL)')
parser.add_argument(
'--os_auth_url',
help=argparse.SUPPRESS)
parser.add_argument(
'--os-tenant-name', metavar='<auth-tenant-name>',
default=env('OS_TENANT_NAME'),
help='Authentication tenant name (Env: OS_TENANT_NAME)')
parser.add_argument(
'--os_tenant_name',
help=argparse.SUPPRESS)
parser.add_argument(
'--os-username', metavar='<auth-username>',
default=utils.env('OS_USERNAME'),
help='Authentication username (Env: OS_USERNAME)')
parser.add_argument(
'--os_username',
help=argparse.SUPPRESS)
parser.add_argument(
'--os-password', metavar='<auth-password>',
default=utils.env('OS_PASSWORD'),
help='Authentication password (Env: OS_PASSWORD)')
parser.add_argument(
'--os_password',
help=argparse.SUPPRESS)
parser.add_argument(
'--os-region-name', metavar='<auth-region-name>',
default=env('OS_REGION_NAME'),
help='Authentication region name (Env: OS_REGION_NAME)')
parser.add_argument(
'--os_region_name',
help=argparse.SUPPRESS)
parser.add_argument(
'--os-token', metavar='<token>',
default=env('OS_TOKEN'),
help='Defaults to env[OS_TOKEN]')
parser.add_argument(
'--os_token',
help=argparse.SUPPRESS)
parser.add_argument(
'--endpoint-type', metavar='<endpoint-type>',
default=env('OS_ENDPOINT_TYPE', default='publicURL'),
help='Defaults to env[OS_ENDPOINT_TYPE] or publicURL.')
parser.add_argument(
'--os-url', metavar='<url>',
default=env('OS_URL'),
help='Defaults to env[OS_URL]')
parser.add_argument(
'--os_url',
help=argparse.SUPPRESS)
parser.add_argument(
'--insecure',
action='store_true',
default=env('QUANTUMCLIENT_INSECURE', default=False),
help="Explicitly allow quantumclient to perform \"insecure\" "
"SSL (https) requests. The server's certificate will "
"not be verified against any certificate authorities. "
"This option should be used with caution.")
return parser
def _bash_completion(self):
"""Prints all of the commands and options for bash-completion."""
commands = set()
options = set()
for option, _action in self.parser._option_string_actions.items():
options.add(option)
for command_name, command in self.command_manager:
commands.add(command_name)
cmd_factory = command.load()
cmd = cmd_factory(self, None)
cmd_parser = cmd.get_parser('')
for option, _action in cmd_parser._option_string_actions.items():
options.add(option)
print ' '.join(commands | options)
def run(self, argv):
"""Equivalent to the main program for the application.
:param argv: input arguments and options
:paramtype argv: list of str
"""
try:
index = 0
command_pos = -1
help_pos = -1
help_command_pos = -1
for arg in argv:
if arg == 'bash-completion':
self._bash_completion()
return 0
if arg in self.commands[self.api_version]:
if command_pos == -1:
command_pos = index
elif arg in ('-h', '--help'):
if help_pos == -1:
help_pos = index
elif arg == 'help':
if help_command_pos == -1:
help_command_pos = index
index = index + 1
if command_pos > -1 and help_pos > command_pos:
argv = ['help', argv[command_pos]]
if help_command_pos > -1 and command_pos == -1:
argv[help_command_pos] = '--help'
self.options, remainder = self.parser.parse_known_args(argv)
self.configure_logging()
self.interactive_mode = not remainder
self.initialize_app(remainder)
except Exception as err:
if self.options.debug:
self.log.exception(unicode(err))
raise
else:
self.log.error(unicode(err))
return 1
result = 1
if self.interactive_mode:
_argv = [sys.argv[0]]
sys.argv = _argv
result = self.interact()
else:
result = self.run_subcommand(remainder)
return result
def run_subcommand(self, argv):
subcommand = self.command_manager.find_command(argv)
cmd_factory, cmd_name, sub_argv = subcommand
cmd = cmd_factory(self, self.options)
err = None
result = 1
try:
self.prepare_to_run_command(cmd)
full_name = (cmd_name
if self.interactive_mode
else ' '.join([self.NAME, cmd_name])
)
cmd_parser = cmd.get_parser(full_name)
return run_command(cmd, cmd_parser, sub_argv)
except Exception as err:
if self.options.debug:
self.log.exception(unicode(err))
else:
self.log.error(unicode(err))
try:
self.clean_up(cmd, result, err)
except Exception as err2:
if self.options.debug:
self.log.exception(unicode(err2))
else:
self.log.error('Could not clean up: %s', unicode(err2))
if self.options.debug:
raise
else:
try:
self.clean_up(cmd, result, None)
except Exception as err3:
if self.options.debug:
self.log.exception(unicode(err3))
else:
self.log.error('Could not clean up: %s', unicode(err3))
return result
def authenticate_user(self):
"""Make sure the user has provided all of the authentication
info we need.
"""
if self.options.os_auth_strategy == 'keystone':
if self.options.os_token or self.options.os_url:
# Token flow auth takes priority
if not self.options.os_token:
raise exc.CommandError(
"You must provide a token via"
" either --os-token or env[OS_TOKEN]")
if not self.options.os_url:
raise exc.CommandError(
"You must provide a service URL via"
" either --os-url or env[OS_URL]")
else:
# Validate password flow auth
if not self.options.os_username:
raise exc.CommandError(
"You must provide a username via"
" either --os-username or env[OS_USERNAME]")
if not self.options.os_password:
raise exc.CommandError(
"You must provide a password via"
" either --os-password or env[OS_PASSWORD]")
if not (self.options.os_tenant_name):
raise exc.CommandError(
"You must provide a tenant_name via"
" either --os-tenant-name or via env[OS_TENANT_NAME]")
if not self.options.os_auth_url:
raise exc.CommandError(
"You must provide an auth url via"
" either --os-auth-url or via env[OS_AUTH_URL]")
else: # not keystone
if not self.options.os_url:
raise exc.CommandError(
"You must provide a service URL via"
" either --os-url or env[OS_URL]")
self.client_manager = clientmanager.ClientManager(
token=self.options.os_token,
url=self.options.os_url,
auth_url=self.options.os_auth_url,
tenant_name=self.options.os_tenant_name,
username=self.options.os_username,
password=self.options.os_password,
region_name=self.options.os_region_name,
api_version=self.api_version,
auth_strategy=self.options.os_auth_strategy,
endpoint_type=self.options.endpoint_type,
insecure=self.options.insecure, )
return
def initialize_app(self, argv):
"""Global app init bits:
* set up API versions
* validate authentication info
"""
super(QuantumShell, self).initialize_app(argv)
self.api_version = {'network': self.api_version}
# If the user is not asking for help, make sure they
# have given us auth.
cmd_name = None
if argv:
cmd_info = self.command_manager.find_command(argv)
cmd_factory, cmd_name, sub_argv = cmd_info
if self.interactive_mode or cmd_name != 'help':
self.authenticate_user()
def clean_up(self, cmd, result, err):
self.log.debug('clean_up %s', cmd.__class__.__name__)
if err:
self.log.debug('got an error: %s', unicode(err))
def configure_logging(self):
"""Create logging handlers for any log output.
"""
root_logger = logging.getLogger('')
# Set up logging to a file
root_logger.setLevel(logging.DEBUG)
# Send higher-level messages to the console via stderr
console = logging.StreamHandler(self.stderr)
console_level = {0: logging.WARNING,
1: logging.INFO,
2: logging.DEBUG,
}.get(self.options.verbose_level, logging.DEBUG)
console.setLevel(console_level)
if logging.DEBUG == console_level:
formatter = logging.Formatter(self.DEBUG_MESSAGE_FORMAT)
else:
formatter = logging.Formatter(self.CONSOLE_MESSAGE_FORMAT)
console.setFormatter(formatter)
root_logger.addHandler(console)
return
from neutronclient import shell
def main(argv=sys.argv[1:]):
try:
return QuantumShell(QUANTUM_API_VERSION).run(map(strutils.safe_decode,
argv))
except exc.QuantumClientException:
return 1
except Exception as e:
print unicode(e)
return 1
shell.main(argv)
if __name__ == "__main__":
sys.exit(main(sys.argv[1:]))
env = shell.env
QuantumShell = shell.NeutronShell
QUANTUM_API_VERSION = shell.NEUTRON_API_VERSION

View File

@@ -1,45 +0,0 @@
# Copyright 2013 OpenStack LLC.
# 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.
#
# vim: tabstop=4 shiftwidth=4 softtabstop=4
import testtools
from quantumclient.common import utils
class UtilsTest(testtools.TestCase):
def test_safe_encode_list(self):
o = object()
unicode_text = u'\u7f51\u7edc'
l = ['abc', unicode_text, unicode_text.encode('utf-8'), o]
expected = ['abc', unicode_text.encode('utf-8'),
unicode_text.encode('utf-8'), o]
self.assertEqual(utils.safe_encode_list(l), expected)
def test_safe_encode_dict(self):
o = object()
unicode_text = u'\u7f51\u7edc'
d = {'test1': unicode_text,
'test2': [unicode_text, o],
'test3': o,
'test4': {'test5': unicode_text},
'test6': unicode_text.encode('utf-8')}
expected = {'test1': unicode_text.encode('utf-8'),
'test2': [unicode_text.encode('utf-8'), o],
'test3': o,
'test4': {'test5': unicode_text.encode('utf-8')},
'test6': unicode_text.encode('utf-8')}
self.assertEqual(utils.safe_encode_dict(d), expected)

View File

@@ -15,866 +15,6 @@
#
# vim: tabstop=4 shiftwidth=4 softtabstop=4
import httplib
import logging
import time
import urllib
import urlparse
from neutronclient.v2_0 import client
from quantumclient import client
from quantumclient.common import _
from quantumclient.common import constants
from quantumclient.common import exceptions
from quantumclient.common import serializer
from quantumclient.common import utils
_logger = logging.getLogger(__name__)
def exception_handler_v20(status_code, error_content):
"""Exception handler for API v2.0 client
This routine generates the appropriate
Quantum exception according to the contents of the
response body
:param status_code: HTTP error status code
:param error_content: deserialized body of error response
"""
quantum_errors = {
'NetworkNotFound': exceptions.NetworkNotFoundClient,
'NetworkInUse': exceptions.NetworkInUseClient,
'PortNotFound': exceptions.PortNotFoundClient,
'RequestedStateInvalid': exceptions.StateInvalidClient,
'PortInUse': exceptions.PortInUseClient,
'AlreadyAttached': exceptions.AlreadyAttachedClient, }
error_dict = None
if isinstance(error_content, dict):
error_dict = error_content.get('QuantumError')
# Find real error type
bad_quantum_error_flag = False
if error_dict:
# If QuantumError key is found, it will definitely contain
# a 'message' and 'type' keys?
try:
error_type = error_dict['type']
error_message = (error_dict['message'] + "\n" +
error_dict['detail'])
except Exception:
bad_quantum_error_flag = True
if not bad_quantum_error_flag:
ex = None
try:
# raise the appropriate error!
ex = quantum_errors[error_type](message=error_message)
ex.args = ([dict(status_code=status_code,
message=error_message)], )
except Exception:
pass
if ex:
raise ex
else:
raise exceptions.QuantumClientException(status_code=status_code,
message=error_dict)
else:
message = None
if isinstance(error_content, dict):
message = error_content.get('message', None)
if message:
raise exceptions.QuantumClientException(status_code=status_code,
message=message)
# If we end up here the exception was not a quantum error
msg = "%s-%s" % (status_code, error_content)
raise exceptions.QuantumClientException(status_code=status_code,
message=msg)
class APIParamsCall(object):
"""A Decorator to add support for format and tenant overriding
and filters
"""
def __init__(self, function):
self.function = function
def __get__(self, instance, owner):
def with_params(*args, **kwargs):
_format = instance.format
if 'format' in kwargs:
instance.format = kwargs['format']
ret = self.function(instance, *args, **kwargs)
instance.format = _format
return ret
return with_params
class Client(object):
"""Client for the OpenStack Quantum v2.0 API.
:param string username: Username for authentication. (optional)
:param string password: Password for authentication. (optional)
:param string token: Token for authentication. (optional)
:param string tenant_name: Tenant name. (optional)
:param string auth_url: Keystone service endpoint for authorization.
:param string endpoint_type: Network service endpoint type to pull from the
keystone catalog (e.g. 'publicURL',
'internalURL', or 'adminURL') (optional)
:param string region_name: Name of a region to select when choosing an
endpoint from the service catalog.
:param string endpoint_url: A user-supplied endpoint URL for the quantum
service. Lazy-authentication is possible for API
service calls if endpoint is set at
instantiation.(optional)
:param integer timeout: Allows customization of the timeout for client
http requests. (optional)
:param insecure: ssl certificate validation. (optional)
Example::
from quantumclient.v2_0 import client
quantum = client.Client(username=USER,
password=PASS,
tenant_name=TENANT_NAME,
auth_url=KEYSTONE_URL)
nets = quantum.list_networks()
...
"""
networks_path = "/networks"
network_path = "/networks/%s"
ports_path = "/ports"
port_path = "/ports/%s"
subnets_path = "/subnets"
subnet_path = "/subnets/%s"
quotas_path = "/quotas"
quota_path = "/quotas/%s"
extensions_path = "/extensions"
extension_path = "/extensions/%s"
routers_path = "/routers"
router_path = "/routers/%s"
floatingips_path = "/floatingips"
floatingip_path = "/floatingips/%s"
security_groups_path = "/security-groups"
security_group_path = "/security-groups/%s"
security_group_rules_path = "/security-group-rules"
security_group_rule_path = "/security-group-rules/%s"
vips_path = "/lb/vips"
vip_path = "/lb/vips/%s"
pools_path = "/lb/pools"
pool_path = "/lb/pools/%s"
pool_path_stats = "/lb/pools/%s/stats"
members_path = "/lb/members"
member_path = "/lb/members/%s"
health_monitors_path = "/lb/health_monitors"
health_monitor_path = "/lb/health_monitors/%s"
associate_pool_health_monitors_path = "/lb/pools/%s/health_monitors"
disassociate_pool_health_monitors_path = (
"/lb/pools/%(pool)s/health_monitors/%(health_monitor)s")
qos_queues_path = "/qos-queues"
qos_queue_path = "/qos-queues/%s"
agents_path = "/agents"
agent_path = "/agents/%s"
network_gateways_path = "/network-gateways"
network_gateway_path = "/network-gateways/%s"
DHCP_NETS = '/dhcp-networks'
DHCP_AGENTS = '/dhcp-agents'
L3_ROUTERS = '/l3-routers'
L3_AGENTS = '/l3-agents'
# API has no way to report plurals, so we have to hard code them
EXTED_PLURALS = {'routers': 'router',
'floatingips': 'floatingip',
'service_types': 'service_type',
'service_definitions': 'service_definition',
'security_groups': 'security_group',
'security_group_rules': 'security_group_rule',
'vips': 'vip',
'pools': 'pool',
'members': 'member',
'health_monitors': 'health_monitor',
'quotas': 'quota',
}
# 8192 Is the default max URI len for eventlet.wsgi.server
MAX_URI_LEN = 8192
def get_attr_metadata(self):
if self.format == 'json':
return {}
old_request_format = self.format
self.format = 'json'
exts = self.list_extensions()['extensions']
self.format = old_request_format
ns = dict([(ext['alias'], ext['namespace']) for ext in exts])
self.EXTED_PLURALS.update(constants.PLURALS)
return {'plurals': self.EXTED_PLURALS,
'xmlns': constants.XML_NS_V20,
constants.EXT_NS: ns}
@APIParamsCall
def get_quotas_tenant(self, **_params):
"""Fetch tenant info in server's context for
following quota operation.
"""
return self.get(self.quota_path % 'tenant', params=_params)
@APIParamsCall
def list_quotas(self, **_params):
"""Fetch all tenants' quotas."""
return self.get(self.quotas_path, params=_params)
@APIParamsCall
def show_quota(self, tenant_id, **_params):
"""Fetch information of a certain tenant's quotas."""
return self.get(self.quota_path % (tenant_id), params=_params)
@APIParamsCall
def update_quota(self, tenant_id, body=None):
"""Update a tenant's quotas."""
return self.put(self.quota_path % (tenant_id), body=body)
@APIParamsCall
def delete_quota(self, tenant_id):
"""Delete the specified tenant's quota values."""
return self.delete(self.quota_path % (tenant_id))
@APIParamsCall
def list_extensions(self, **_params):
"""Fetch a list of all exts on server side."""
return self.get(self.extensions_path, params=_params)
@APIParamsCall
def show_extension(self, ext_alias, **_params):
"""Fetch a list of all exts on server side."""
return self.get(self.extension_path % ext_alias, params=_params)
@APIParamsCall
def list_ports(self, retrieve_all=True, **_params):
"""Fetches a list of all networks for a tenant."""
# Pass filters in "params" argument to do_request
return self.list('ports', self.ports_path, retrieve_all,
**_params)
@APIParamsCall
def show_port(self, port, **_params):
"""Fetches information of a certain network."""
return self.get(self.port_path % (port), params=_params)
@APIParamsCall
def create_port(self, body=None):
"""Creates a new port."""
return self.post(self.ports_path, body=body)
@APIParamsCall
def update_port(self, port, body=None):
"""Updates a port."""
return self.put(self.port_path % (port), body=body)
@APIParamsCall
def delete_port(self, port):
"""Deletes the specified port."""
return self.delete(self.port_path % (port))
@APIParamsCall
def list_networks(self, retrieve_all=True, **_params):
"""Fetches a list of all networks for a tenant."""
# Pass filters in "params" argument to do_request
return self.list('networks', self.networks_path, retrieve_all,
**_params)
@APIParamsCall
def show_network(self, network, **_params):
"""Fetches information of a certain network."""
return self.get(self.network_path % (network), params=_params)
@APIParamsCall
def create_network(self, body=None):
"""Creates a new network."""
return self.post(self.networks_path, body=body)
@APIParamsCall
def update_network(self, network, body=None):
"""Updates a network."""
return self.put(self.network_path % (network), body=body)
@APIParamsCall
def delete_network(self, network):
"""Deletes the specified network."""
return self.delete(self.network_path % (network))
@APIParamsCall
def list_subnets(self, retrieve_all=True, **_params):
"""Fetches a list of all networks for a tenant."""
return self.list('subnets', self.subnets_path, retrieve_all,
**_params)
@APIParamsCall
def show_subnet(self, subnet, **_params):
"""Fetches information of a certain subnet."""
return self.get(self.subnet_path % (subnet), params=_params)
@APIParamsCall
def create_subnet(self, body=None):
"""Creates a new subnet."""
return self.post(self.subnets_path, body=body)
@APIParamsCall
def update_subnet(self, subnet, body=None):
"""Updates a subnet."""
return self.put(self.subnet_path % (subnet), body=body)
@APIParamsCall
def delete_subnet(self, subnet):
"""Deletes the specified subnet."""
return self.delete(self.subnet_path % (subnet))
@APIParamsCall
def list_routers(self, retrieve_all=True, **_params):
"""Fetches a list of all routers for a tenant."""
# Pass filters in "params" argument to do_request
return self.list('routers', self.routers_path, retrieve_all,
**_params)
@APIParamsCall
def show_router(self, router, **_params):
"""Fetches information of a certain router."""
return self.get(self.router_path % (router), params=_params)
@APIParamsCall
def create_router(self, body=None):
"""Creates a new router."""
return self.post(self.routers_path, body=body)
@APIParamsCall
def update_router(self, router, body=None):
"""Updates a router."""
return self.put(self.router_path % (router), body=body)
@APIParamsCall
def delete_router(self, router):
"""Deletes the specified router."""
return self.delete(self.router_path % (router))
@APIParamsCall
def add_interface_router(self, router, body=None):
"""Adds an internal network interface to the specified router."""
return self.put((self.router_path % router) + "/add_router_interface",
body=body)
@APIParamsCall
def remove_interface_router(self, router, body=None):
"""Removes an internal network interface from the specified router."""
return self.put((self.router_path % router) +
"/remove_router_interface", body=body)
@APIParamsCall
def add_gateway_router(self, router, body=None):
"""Adds an external network gateway to the specified router."""
return self.put((self.router_path % router),
body={'router': {'external_gateway_info': body}})
@APIParamsCall
def remove_gateway_router(self, router):
"""Removes an external network gateway from the specified router."""
return self.put((self.router_path % router),
body={'router': {'external_gateway_info': {}}})
@APIParamsCall
def list_floatingips(self, retrieve_all=True, **_params):
"""Fetches a list of all floatingips for a tenant."""
# Pass filters in "params" argument to do_request
return self.list('floatingips', self.floatingips_path, retrieve_all,
**_params)
@APIParamsCall
def show_floatingip(self, floatingip, **_params):
"""Fetches information of a certain floatingip."""
return self.get(self.floatingip_path % (floatingip), params=_params)
@APIParamsCall
def create_floatingip(self, body=None):
"""Creates a new floatingip."""
return self.post(self.floatingips_path, body=body)
@APIParamsCall
def update_floatingip(self, floatingip, body=None):
"""Updates a floatingip."""
return self.put(self.floatingip_path % (floatingip), body=body)
@APIParamsCall
def delete_floatingip(self, floatingip):
"""Deletes the specified floatingip."""
return self.delete(self.floatingip_path % (floatingip))
@APIParamsCall
def create_security_group(self, body=None):
"""Creates a new security group."""
return self.post(self.security_groups_path, body=body)
@APIParamsCall
def update_security_group(self, security_group, body=None):
"""Updates a security group."""
return self.put(self.security_group_path %
security_group, body=body)
@APIParamsCall
def list_security_groups(self, retrieve_all=True, **_params):
"""Fetches a list of all security groups for a tenant."""
return self.list('security_groups', self.security_groups_path,
retrieve_all, **_params)
@APIParamsCall
def show_security_group(self, security_group, **_params):
"""Fetches information of a certain security group."""
return self.get(self.security_group_path % (security_group),
params=_params)
@APIParamsCall
def delete_security_group(self, security_group):
"""Deletes the specified security group."""
return self.delete(self.security_group_path % (security_group))
@APIParamsCall
def create_security_group_rule(self, body=None):
"""Creates a new security group rule."""
return self.post(self.security_group_rules_path, body=body)
@APIParamsCall
def delete_security_group_rule(self, security_group_rule):
"""Deletes the specified security group rule."""
return self.delete(self.security_group_rule_path %
(security_group_rule))
@APIParamsCall
def list_security_group_rules(self, retrieve_all=True, **_params):
"""Fetches a list of all security group rules for a tenant."""
return self.list('security_group_rules',
self.security_group_rules_path,
retrieve_all, **_params)
@APIParamsCall
def show_security_group_rule(self, security_group_rule, **_params):
"""Fetches information of a certain security group rule."""
return self.get(self.security_group_rule_path % (security_group_rule),
params=_params)
@APIParamsCall
def list_vips(self, retrieve_all=True, **_params):
"""Fetches a list of all load balancer vips for a tenant."""
# Pass filters in "params" argument to do_request
return self.list('vips', self.vips_path, retrieve_all,
**_params)
@APIParamsCall
def show_vip(self, vip, **_params):
"""Fetches information of a certain load balancer vip."""
return self.get(self.vip_path % (vip), params=_params)
@APIParamsCall
def create_vip(self, body=None):
"""Creates a new load balancer vip."""
return self.post(self.vips_path, body=body)
@APIParamsCall
def update_vip(self, vip, body=None):
"""Updates a load balancer vip."""
return self.put(self.vip_path % (vip), body=body)
@APIParamsCall
def delete_vip(self, vip):
"""Deletes the specified load balancer vip."""
return self.delete(self.vip_path % (vip))
@APIParamsCall
def list_pools(self, retrieve_all=True, **_params):
"""Fetches a list of all load balancer pools for a tenant."""
# Pass filters in "params" argument to do_request
return self.list('pools', self.pools_path, retrieve_all,
**_params)
@APIParamsCall
def show_pool(self, pool, **_params):
"""Fetches information of a certain load balancer pool."""
return self.get(self.pool_path % (pool), params=_params)
@APIParamsCall
def create_pool(self, body=None):
"""Creates a new load balancer pool."""
return self.post(self.pools_path, body=body)
@APIParamsCall
def update_pool(self, pool, body=None):
"""Updates a load balancer pool."""
return self.put(self.pool_path % (pool), body=body)
@APIParamsCall
def delete_pool(self, pool):
"""Deletes the specified load balancer pool."""
return self.delete(self.pool_path % (pool))
@APIParamsCall
def retrieve_pool_stats(self, pool, **_params):
"""Retrieves stats for a certain load balancer pool."""
return self.get(self.pool_path_stats % (pool), params=_params)
@APIParamsCall
def list_members(self, retrieve_all=True, **_params):
"""Fetches a list of all load balancer members for a tenant."""
# Pass filters in "params" argument to do_request
return self.list('members', self.members_path, retrieve_all,
**_params)
@APIParamsCall
def show_member(self, member, **_params):
"""Fetches information of a certain load balancer member."""
return self.get(self.member_path % (member), params=_params)
@APIParamsCall
def create_member(self, body=None):
"""Creates a new load balancer member."""
return self.post(self.members_path, body=body)
@APIParamsCall
def update_member(self, member, body=None):
"""Updates a load balancer member."""
return self.put(self.member_path % (member), body=body)
@APIParamsCall
def delete_member(self, member):
"""Deletes the specified load balancer member."""
return self.delete(self.member_path % (member))
@APIParamsCall
def list_health_monitors(self, retrieve_all=True, **_params):
"""Fetches a list of all load balancer health monitors for a tenant."""
# Pass filters in "params" argument to do_request
return self.list('health_monitors', self.health_monitors_path,
retrieve_all, **_params)
@APIParamsCall
def show_health_monitor(self, health_monitor, **_params):
"""Fetches information of a certain load balancer health monitor."""
return self.get(self.health_monitor_path % (health_monitor),
params=_params)
@APIParamsCall
def create_health_monitor(self, body=None):
"""Creates a new load balancer health monitor."""
return self.post(self.health_monitors_path, body=body)
@APIParamsCall
def update_health_monitor(self, health_monitor, body=None):
"""Updates a load balancer health monitor."""
return self.put(self.health_monitor_path % (health_monitor), body=body)
@APIParamsCall
def delete_health_monitor(self, health_monitor):
"""Deletes the specified load balancer health monitor."""
return self.delete(self.health_monitor_path % (health_monitor))
@APIParamsCall
def associate_health_monitor(self, pool, body):
"""Associate specified load balancer health monitor and pool."""
return self.post(self.associate_pool_health_monitors_path % (pool),
body=body)
@APIParamsCall
def disassociate_health_monitor(self, pool, health_monitor):
"""Disassociate specified load balancer health monitor and pool."""
path = (self.disassociate_pool_health_monitors_path %
{'pool': pool, 'health_monitor': health_monitor})
return self.delete(path)
@APIParamsCall
def create_qos_queue(self, body=None):
"""Creates a new queue."""
return self.post(self.qos_queues_path, body=body)
@APIParamsCall
def list_qos_queues(self, **_params):
"""Fetches a list of all queues for a tenant."""
return self.get(self.qos_queues_path, params=_params)
@APIParamsCall
def show_qos_queue(self, queue, **_params):
"""Fetches information of a certain queue."""
return self.get(self.qos_queue_path % (queue),
params=_params)
@APIParamsCall
def delete_qos_queue(self, queue):
"""Deletes the specified queue."""
return self.delete(self.qos_queue_path % (queue))
@APIParamsCall
def list_agents(self, **_params):
"""Fetches agents."""
# Pass filters in "params" argument to do_request
return self.get(self.agents_path, params=_params)
@APIParamsCall
def show_agent(self, agent, **_params):
"""Fetches information of a certain agent."""
return self.get(self.agent_path % (agent), params=_params)
@APIParamsCall
def update_agent(self, agent, body=None):
"""Updates an agent."""
return self.put(self.agent_path % (agent), body=body)
@APIParamsCall
def delete_agent(self, agent):
"""Deletes the specified agent."""
return self.delete(self.agent_path % (agent))
@APIParamsCall
def list_network_gateways(self, **_params):
"""Retrieve network gateways."""
return self.get(self.network_gateways_path, params=_params)
@APIParamsCall
def show_network_gateway(self, gateway_id, **_params):
"""Fetch a network gateway."""
return self.get(self.network_gateway_path % gateway_id, params=_params)
@APIParamsCall
def create_network_gateway(self, body=None):
"""Create a new network gateway."""
return self.post(self.network_gateways_path, body=body)
@APIParamsCall
def update_network_gateway(self, gateway_id, body=None):
"""Update a network gateway."""
return self.put(self.network_gateway_path % gateway_id, body=body)
@APIParamsCall
def delete_network_gateway(self, gateway_id):
"""Delete the specified network gateway."""
return self.delete(self.network_gateway_path % gateway_id)
@APIParamsCall
def connect_network_gateway(self, gateway_id, body=None):
"""Connect a network gateway to the specified network."""
base_uri = self.network_gateway_path % gateway_id
return self.put("%s/connect_network" % base_uri, body=body)
@APIParamsCall
def disconnect_network_gateway(self, gateway_id, body=None):
"""Disconnect a network from the specified gateway."""
base_uri = self.network_gateway_path % gateway_id
return self.put("%s/disconnect_network" % base_uri, body=body)
@APIParamsCall
def list_dhcp_agent_hosting_networks(self, network, **_params):
"""Fetches a list of dhcp agents hosting a network."""
return self.get((self.network_path + self.DHCP_AGENTS) % network,
params=_params)
@APIParamsCall
def list_networks_on_dhcp_agent(self, dhcp_agent, **_params):
"""Fetches a list of dhcp agents hosting a network."""
return self.get((self.agent_path + self.DHCP_NETS) % dhcp_agent,
params=_params)
@APIParamsCall
def add_network_to_dhcp_agent(self, dhcp_agent, body=None):
"""Adds a network to dhcp agent."""
return self.post((self.agent_path + self.DHCP_NETS) % dhcp_agent,
body=body)
@APIParamsCall
def remove_network_from_dhcp_agent(self, dhcp_agent, network_id):
"""Remove a network from dhcp agent."""
return self.delete((self.agent_path + self.DHCP_NETS + "/%s") % (
dhcp_agent, network_id))
@APIParamsCall
def list_l3_agent_hosting_routers(self, router, **_params):
"""Fetches a list of L3 agents hosting a router."""
return self.get((self.router_path + self.L3_AGENTS) % router,
params=_params)
@APIParamsCall
def list_routers_on_l3_agent(self, l3_agent, **_params):
"""Fetches a list of L3 agents hosting a router."""
return self.get((self.agent_path + self.L3_ROUTERS) % l3_agent,
params=_params)
@APIParamsCall
def add_router_to_l3_agent(self, l3_agent, body):
"""Adds a router to L3 agent."""
return self.post((self.agent_path + self.L3_ROUTERS) % l3_agent,
body=body)
@APIParamsCall
def remove_router_from_l3_agent(self, l3_agent, router_id):
"""Remove a router from l3 agent."""
return self.delete((self.agent_path + self.L3_ROUTERS + "/%s") % (
l3_agent, router_id))
def __init__(self, **kwargs):
"""Initialize a new client for the Quantum v2.0 API."""
super(Client, self).__init__()
self.httpclient = client.HTTPClient(**kwargs)
self.version = '2.0'
self.format = 'json'
self.action_prefix = "/v%s" % (self.version)
self.retries = 0
self.retry_interval = 1
def _handle_fault_response(self, status_code, response_body):
# Create exception with HTTP status code and message
_logger.debug("Error message: %s", response_body)
# Add deserialized error message to exception arguments
try:
des_error_body = self.deserialize(response_body, status_code)
except Exception:
# If unable to deserialized body it is probably not a
# Quantum error
des_error_body = {'message': response_body}
# Raise the appropriate exception
exception_handler_v20(status_code, des_error_body)
def _check_uri_length(self, action):
uri_len = len(self.httpclient.endpoint_url) + len(action)
if uri_len > self.MAX_URI_LEN:
raise exceptions.RequestURITooLong(
excess=uri_len - self.MAX_URI_LEN)
def do_request(self, method, action, body=None, headers=None, params=None):
# Add format and tenant_id
action += ".%s" % self.format
action = self.action_prefix + action
if type(params) is dict and params:
params = utils.safe_encode_dict(params)
action += '?' + urllib.urlencode(params, doseq=1)
# Ensure client always has correct uri - do not guesstimate anything
self.httpclient.authenticate_and_fetch_endpoint_url()
self._check_uri_length(action)
if body:
body = self.serialize(body)
self.httpclient.content_type = self.content_type()
resp, replybody = self.httpclient.do_request(action, method, body=body)
status_code = self.get_status_code(resp)
if status_code in (httplib.OK,
httplib.CREATED,
httplib.ACCEPTED,
httplib.NO_CONTENT):
return self.deserialize(replybody, status_code)
else:
self._handle_fault_response(status_code, replybody)
def get_auth_info(self):
return self.httpclient.get_auth_info()
def get_status_code(self, response):
"""Returns the integer status code from the response.
Either a Webob.Response (used in testing) or httplib.Response
is returned.
"""
if hasattr(response, 'status_int'):
return response.status_int
else:
return response.status
def serialize(self, data):
"""Serializes a dictionary into either xml or json.
A dictionary with a single key can be passed and
it can contain any structure.
"""
if data is None:
return None
elif type(data) is dict:
return serializer.Serializer(
self.get_attr_metadata()).serialize(data, self.content_type())
else:
raise Exception("unable to serialize object of type = '%s'" %
type(data))
def deserialize(self, data, status_code):
"""Deserializes an xml or json string into a dictionary."""
if status_code == 204:
return data
return serializer.Serializer(self.get_attr_metadata()).deserialize(
data, self.content_type())['body']
def content_type(self, _format=None):
"""Returns the mime-type for either 'xml' or 'json'.
Defaults to the currently set format.
"""
_format = _format or self.format
return "application/%s" % (_format)
def retry_request(self, method, action, body=None,
headers=None, params=None):
"""Call do_request with the default retry configuration.
Only idempotent requests should retry failed connection attempts.
:raises: ConnectionFailed if the maximum # of retries is exceeded
"""
max_attempts = self.retries + 1
for i in xrange(max_attempts):
try:
return self.do_request(method, action, body=body,
headers=headers, params=params)
except exceptions.ConnectionFailed:
# Exception has already been logged by do_request()
if i < self.retries:
_logger.debug(_('Retrying connection to quantum service'))
time.sleep(self.retry_interval)
raise exceptions.ConnectionFailed(reason=_("Maximum attempts reached"))
def delete(self, action, body=None, headers=None, params=None):
return self.retry_request("DELETE", action, body=body,
headers=headers, params=params)
def get(self, action, body=None, headers=None, params=None):
return self.retry_request("GET", action, body=body,
headers=headers, params=params)
def post(self, action, body=None, headers=None, params=None):
# Do not retry POST requests to avoid the orphan objects problem.
return self.do_request("POST", action, body=body,
headers=headers, params=params)
def put(self, action, body=None, headers=None, params=None):
return self.retry_request("PUT", action, body=body,
headers=headers, params=params)
def list(self, collection, path, retrieve_all=True, **params):
if retrieve_all:
res = []
for r in self._pagination(collection, path, **params):
res.extend(r[collection])
return {collection: res}
else:
return self._pagination(collection, path, **params)
def _pagination(self, collection, path, **params):
if params.get('page_reverse', False):
linkrel = 'previous'
else:
linkrel = 'next'
next = True
while next:
res = self.get(path, params=params)
yield res
next = False
try:
for link in res['%s_links' % collection]:
if link['rel'] == linkrel:
query_str = urlparse.urlparse(link['href']).query
params = urlparse.parse_qs(query_str)
next = True
break
except KeyError:
break
Client = client.Client

View File

@@ -1,9 +1,2 @@
d2to1>=0.2.10,<0.3
pbr>=0.5.16,<0.6
argparse
cliff>=1.4
httplib2
iso8601
prettytable>=0.6,<0.8
pyparsing>=1.5.6,<2.0
simplejson
pbr>=0.5.21,<1.0
python-neutronclient>=2.3.0,<3

View File

@@ -19,5 +19,5 @@
import setuptools
setuptools.setup(
setup_requires=['d2to1>=0.2.10,<0.3', 'pbr>=0.5.10,<0.6'],
d2to1=True)
setup_requires=['pbr>=0.5.21,<1.0'],
pbr=True)

View File

@@ -1,15 +1,9 @@
# Install bounded pep8/pyflakes first, then let flake8 install
pep8==1.4.5
pyflakes==0.7.2
flake8==2.0
hacking>=0.5.3,<0.6
hacking>=0.5.6,<0.8
cliff-tablib>=1.0
coverage
coverage>=3.6
discover
fixtures>=0.3.12
mox
fixtures>=0.3.14
python-subunit
sphinx>=1.1.2
testrepository>=0.0.13
testtools>=0.9.22
testrepository>=0.0.17
testtools>=0.9.32

View File

@@ -1,16 +0,0 @@
# Copyright 2013 OpenStack LLC.
# 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.
#
# vim: tabstop=4 shiftwidth=4 softtabstop=4

View File

@@ -1,16 +0,0 @@
# Copyright 2013 OpenStack LLC.
# 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.
#
# vim: tabstop=4 shiftwidth=4 softtabstop=4

View File

@@ -1,219 +0,0 @@
# Copyright 2013 Mirantis Inc.
# 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.
#
# @author: Ilya Shakhat, Mirantis Inc.
#
# vim: tabstop=4 shiftwidth=4 softtabstop=4
import sys
import mox
from quantumclient.quantum.v2_0.lb import healthmonitor
from tests.unit import test_cli20
class CLITestV20LbHealthmonitorJSON(test_cli20.CLITestV20Base):
def test_create_healthmonitor_with_mandatory_params(self):
"""lb-healthmonitor-create with mandatory params only."""
resource = 'health_monitor'
cmd = healthmonitor.CreateHealthMonitor(test_cli20.MyApp(sys.stdout),
None)
admin_state_up = False
delay = '60'
max_retries = '2'
timeout = '10'
type = 'tcp'
tenant_id = 'my-tenant'
my_id = 'my-id'
args = ['--admin-state-down',
'--delay', delay,
'--max-retries', max_retries,
'--timeout', timeout,
'--type', type,
'--tenant-id', tenant_id]
position_names = ['admin_state_up', 'delay', 'max_retries', 'timeout',
'type', 'tenant_id']
position_values = [admin_state_up, delay, max_retries, timeout, type,
tenant_id]
self._test_create_resource(resource, cmd, '', my_id, args,
position_names, position_values)
def test_create_healthmonitor_with_all_params(self):
"""lb-healthmonitor-create with all params set."""
resource = 'health_monitor'
cmd = healthmonitor.CreateHealthMonitor(test_cli20.MyApp(sys.stdout),
None)
admin_state_up = False
delay = '60'
expected_codes = '200-202,204'
http_method = 'HEAD'
max_retries = '2'
timeout = '10'
type = 'tcp'
tenant_id = 'my-tenant'
url_path = '/health'
my_id = 'my-id'
args = ['--admin-state-down',
'--delay', delay,
'--expected-codes', expected_codes,
'--http-method', http_method,
'--max-retries', max_retries,
'--timeout', timeout,
'--type', type,
'--tenant-id', tenant_id,
'--url-path', url_path]
position_names = ['admin_state_up', 'delay',
'expected_codes', 'http_method',
'max_retries', 'timeout',
'type', 'tenant_id', 'url_path']
position_values = [admin_state_up, delay,
expected_codes, http_method,
max_retries, timeout,
type, tenant_id, url_path]
self._test_create_resource(resource, cmd, '', my_id, args,
position_names, position_values)
def test_list_healthmonitors(self):
"""lb-healthmonitor-list."""
resources = "health_monitors"
cmd = healthmonitor.ListHealthMonitor(test_cli20.MyApp(sys.stdout),
None)
self._test_list_resources(resources, cmd, True)
def test_list_healthmonitors_pagination(self):
"""lb-healthmonitor-list."""
resources = "health_monitors"
cmd = healthmonitor.ListHealthMonitor(test_cli20.MyApp(sys.stdout),
None)
self._test_list_resources_with_pagination(resources, cmd)
def test_list_healthmonitors_sort(self):
"""lb-healthmonitor-list --sort-key name --sort-key id --sort-key asc
--sort-key desc
"""
resources = "health_monitors"
cmd = healthmonitor.ListHealthMonitor(test_cli20.MyApp(sys.stdout),
None)
self._test_list_resources(resources, cmd,
sort_key=["name", "id"],
sort_dir=["asc", "desc"])
def test_list_healthmonitors_limit(self):
"""lb-healthmonitor-list -P."""
resources = "health_monitors"
cmd = healthmonitor.ListHealthMonitor(test_cli20.MyApp(sys.stdout),
None)
self._test_list_resources(resources, cmd, page_size=1000)
def test_show_healthmonitor_id(self):
"""lb-healthmonitor-show test_id."""
resource = 'health_monitor'
cmd = healthmonitor.ShowHealthMonitor(test_cli20.MyApp(sys.stdout),
None)
args = ['--fields', 'id', self.test_id]
self._test_show_resource(resource, cmd, self.test_id, args, ['id'])
def test_show_healthmonitor_id_name(self):
"""lb-healthmonitor-show."""
resource = 'health_monitor'
cmd = healthmonitor.ShowHealthMonitor(test_cli20.MyApp(sys.stdout),
None)
args = ['--fields', 'id', '--fields', 'name', self.test_id]
self._test_show_resource(resource, cmd, self.test_id,
args, ['id', 'name'])
def test_update_health_monitor(self):
"""lb-healthmonitor-update myid --name myname --tags a b."""
resource = 'health_monitor'
cmd = healthmonitor.UpdateHealthMonitor(test_cli20.MyApp(sys.stdout),
None)
self._test_update_resource(resource, cmd, 'myid',
['myid', '--timeout', '5'],
{'timeout': '5', })
def test_delete_healthmonitor(self):
"""lb-healthmonitor-delete my-id."""
resource = 'health_monitor'
cmd = healthmonitor.DeleteHealthMonitor(test_cli20.MyApp(sys.stdout),
None)
my_id = 'my-id'
args = [my_id]
self._test_delete_resource(resource, cmd, my_id, args)
def test_associate_healthmonitor(self):
cmd = healthmonitor.AssociateHealthMonitor(
test_cli20.MyApp(sys.stdout),
None)
resource = 'health_monitor'
health_monitor_id = 'hm-id'
pool_id = 'p_id'
args = [health_monitor_id, pool_id]
self.mox.StubOutWithMock(cmd, "get_client")
self.mox.StubOutWithMock(self.client.httpclient, "request")
cmd.get_client().MultipleTimes().AndReturn(self.client)
body = {resource: {'id': health_monitor_id}}
result = {resource: {'id': health_monitor_id}, }
result_str = self.client.serialize(result)
path = getattr(self.client,
"associate_pool_health_monitors_path") % pool_id
return_tup = (test_cli20.MyResp(200), result_str)
self.client.httpclient.request(
test_cli20.end_url(path), 'POST',
body=test_cli20.MyComparator(body, self.client),
headers=mox.ContainsKeyValue(
'X-Auth-Token', test_cli20.TOKEN)).AndReturn(return_tup)
self.mox.ReplayAll()
cmd_parser = cmd.get_parser('test_' + resource)
parsed_args = cmd_parser.parse_args(args)
cmd.run(parsed_args)
self.mox.VerifyAll()
self.mox.UnsetStubs()
def test_disassociate_healthmonitor(self):
cmd = healthmonitor.DisassociateHealthMonitor(
test_cli20.MyApp(sys.stdout),
None)
resource = 'health_monitor'
health_monitor_id = 'hm-id'
pool_id = 'p_id'
args = [health_monitor_id, pool_id]
self.mox.StubOutWithMock(cmd, "get_client")
self.mox.StubOutWithMock(self.client.httpclient, "request")
cmd.get_client().MultipleTimes().AndReturn(self.client)
path = (getattr(self.client,
"disassociate_pool_health_monitors_path") %
{'pool': pool_id, 'health_monitor': health_monitor_id})
return_tup = (test_cli20.MyResp(204), None)
self.client.httpclient.request(
test_cli20.end_url(path), 'DELETE',
body=None,
headers=mox.ContainsKeyValue(
'X-Auth-Token', test_cli20.TOKEN)).AndReturn(return_tup)
self.mox.ReplayAll()
cmd_parser = cmd.get_parser('test_' + resource)
parsed_args = cmd_parser.parse_args(args)
cmd.run(parsed_args)
self.mox.VerifyAll()
self.mox.UnsetStubs()
class CLITestV20LbHealthmonitorXML(CLITestV20LbHealthmonitorJSON):
format = 'xml'

View File

@@ -1,134 +0,0 @@
# Copyright 2013 Mirantis Inc.
# 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.
#
# @author: Ilya Shakhat, Mirantis Inc.
#
# vim: tabstop=4 shiftwidth=4 softtabstop=4
import sys
from quantumclient.quantum.v2_0.lb import member
from tests.unit import test_cli20
class CLITestV20LbMemberJSON(test_cli20.CLITestV20Base):
def setUp(self):
super(CLITestV20LbMemberJSON, self).setUp(plurals={'tags': 'tag'})
def test_create_member(self):
"""lb-member-create with mandatory params only."""
resource = 'member'
cmd = member.CreateMember(test_cli20.MyApp(sys.stdout), None)
address = '10.0.0.1'
port = '8080'
tenant_id = 'my-tenant'
my_id = 'my-id'
pool_id = 'pool-id'
args = ['--address', address, '--protocol-port', port,
'--tenant-id', tenant_id, pool_id]
position_names = ['address', 'protocol_port', 'tenant_id', 'pool_id',
'admin_state_up']
position_values = [address, port, tenant_id, pool_id, True]
self._test_create_resource(resource, cmd, None, my_id, args,
position_names, position_values,
admin_state_up=None)
def test_create_member_all_params(self):
"""lb-member-create with all available params."""
resource = 'member'
cmd = member.CreateMember(test_cli20.MyApp(sys.stdout), None)
address = '10.0.0.1'
admin_state_up = False
port = '8080'
weight = '1'
tenant_id = 'my-tenant'
my_id = 'my-id'
pool_id = 'pool-id'
args = ['--address', address, '--admin-state-down',
'--protocol-port', port, '--weight', weight,
'--tenant-id', tenant_id, pool_id]
position_names = [
'address', 'admin_state_up', 'protocol_port', 'weight',
'tenant_id', 'pool_id'
]
position_values = [address, admin_state_up, port, weight,
tenant_id, pool_id]
self._test_create_resource(resource, cmd, None, my_id, args,
position_names, position_values,
admin_state_up=None)
def test_list_members(self):
"""lb-member-list."""
resources = "members"
cmd = member.ListMember(test_cli20.MyApp(sys.stdout), None)
self._test_list_resources(resources, cmd, True)
def test_list_members_pagination(self):
"""lb-member-list."""
resources = "members"
cmd = member.ListMember(test_cli20.MyApp(sys.stdout), None)
self._test_list_resources_with_pagination(resources, cmd)
def test_list_members_sort(self):
"""lb-member-list --sort-key name --sort-key id --sort-key asc
--sort-key desc
"""
resources = "members"
cmd = member.ListMember(test_cli20.MyApp(sys.stdout), None)
self._test_list_resources(resources, cmd,
sort_key=["name", "id"],
sort_dir=["asc", "desc"])
def test_list_members_limit(self):
"""lb-member-list -P."""
resources = "members"
cmd = member.ListMember(test_cli20.MyApp(sys.stdout), None)
self._test_list_resources(resources, cmd, page_size=1000)
def test_show_member_id(self):
"""lb-member-show test_id."""
resource = 'member'
cmd = member.ShowMember(test_cli20.MyApp(sys.stdout), None)
args = ['--fields', 'id', self.test_id]
self._test_show_resource(resource, cmd, self.test_id, args, ['id'])
def test_show_member_id_name(self):
"""lb-member-show."""
resource = 'member'
cmd = member.ShowMember(test_cli20.MyApp(sys.stdout), None)
args = ['--fields', 'id', '--fields', 'name', self.test_id]
self._test_show_resource(resource, cmd, self.test_id,
args, ['id', 'name'])
def test_update_member(self):
"""lb-member-update myid --name myname --tags a b."""
resource = 'member'
cmd = member.UpdateMember(test_cli20.MyApp(sys.stdout), None)
self._test_update_resource(resource, cmd, 'myid',
['myid', '--name', 'myname',
'--tags', 'a', 'b'],
{'name': 'myname', 'tags': ['a', 'b'], })
def test_delete_member(self):
"""lb-member-delete my-id."""
resource = 'member'
cmd = member.DeleteMember(test_cli20.MyApp(sys.stdout), None)
my_id = 'my-id'
args = [my_id]
self._test_delete_resource(resource, cmd, my_id, args)
class CLITestV20LbMemberXML(CLITestV20LbMemberJSON):
format = 'xml'

View File

@@ -1,171 +0,0 @@
# Copyright 2013 Mirantis Inc.
# 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.
#
# @author: Ilya Shakhat, Mirantis Inc.
#
# vim: tabstop=4 shiftwidth=4 softtabstop=4
import sys
import mox
from quantumclient.quantum.v2_0.lb import pool
from tests.unit import test_cli20
class CLITestV20LbPoolJSON(test_cli20.CLITestV20Base):
def test_create_pool_with_mandatory_params(self):
"""lb-pool-create with mandatory params only."""
resource = 'pool'
cmd = pool.CreatePool(test_cli20.MyApp(sys.stdout), None)
name = 'my-name'
lb_method = 'round-robin'
protocol = 'http'
subnet_id = 'subnet-id'
tenant_id = 'my-tenant'
my_id = 'my-id'
args = ['--lb-method', lb_method,
'--name', name,
'--protocol', protocol,
'--subnet-id', subnet_id,
'--tenant-id', tenant_id]
position_names = ['admin_state_up', 'lb_method', 'name',
'protocol', 'subnet_id', 'tenant_id']
position_values = [True, lb_method, name,
protocol, subnet_id, tenant_id]
self._test_create_resource(resource, cmd, name, my_id, args,
position_names, position_values)
def test_create_pool_with_all_params(self):
"""lb-pool-create with all params set."""
resource = 'pool'
cmd = pool.CreatePool(test_cli20.MyApp(sys.stdout), None)
name = 'my-name'
description = 'my-desc'
lb_method = 'round-robin'
protocol = 'http'
subnet_id = 'subnet-id'
tenant_id = 'my-tenant'
my_id = 'my-id'
args = ['--admin-state-down',
'--description', description,
'--lb-method', lb_method,
'--name', name,
'--protocol', protocol,
'--subnet-id', subnet_id,
'--tenant-id', tenant_id]
position_names = ['admin_state_up', 'description', 'lb_method', 'name',
'protocol', 'subnet_id', 'tenant_id']
position_values = [False, description, lb_method, name,
protocol, subnet_id, tenant_id]
self._test_create_resource(resource, cmd, name, my_id, args,
position_names, position_values)
def test_list_pools(self):
"""lb-pool-list."""
resources = "pools"
cmd = pool.ListPool(test_cli20.MyApp(sys.stdout), None)
self._test_list_resources(resources, cmd, True)
def test_list_pools_pagination(self):
"""lb-pool-list."""
resources = "pools"
cmd = pool.ListPool(test_cli20.MyApp(sys.stdout), None)
self._test_list_resources_with_pagination(resources, cmd)
def test_list_pools_sort(self):
"""lb-pool-list --sort-key name --sort-key id --sort-key asc
--sort-key desc
"""
resources = "pools"
cmd = pool.ListPool(test_cli20.MyApp(sys.stdout), None)
self._test_list_resources(resources, cmd,
sort_key=["name", "id"],
sort_dir=["asc", "desc"])
def test_list_pools_limit(self):
"""lb-pool-list -P."""
resources = "pools"
cmd = pool.ListPool(test_cli20.MyApp(sys.stdout), None)
self._test_list_resources(resources, cmd, page_size=1000)
def test_show_pool_id(self):
"""lb-pool-show test_id."""
resource = 'pool'
cmd = pool.ShowPool(test_cli20.MyApp(sys.stdout), None)
args = ['--fields', 'id', self.test_id]
self._test_show_resource(resource, cmd, self.test_id, args, ['id'])
def test_show_pool_id_name(self):
"""lb-pool-show."""
resource = 'pool'
cmd = pool.ShowPool(test_cli20.MyApp(sys.stdout), None)
args = ['--fields', 'id', '--fields', 'name', self.test_id]
self._test_show_resource(resource, cmd, self.test_id,
args, ['id', 'name'])
def test_update_pool(self):
"""lb-pool-update myid --name newname --tags a b."""
resource = 'pool'
cmd = pool.UpdatePool(test_cli20.MyApp(sys.stdout), None)
self._test_update_resource(resource, cmd, 'myid',
['myid', '--name', 'newname'],
{'name': 'newname', })
def test_delete_pool(self):
"""lb-pool-delete my-id."""
resource = 'pool'
cmd = pool.DeletePool(test_cli20.MyApp(sys.stdout), None)
my_id = 'my-id'
args = [my_id]
self._test_delete_resource(resource, cmd, my_id, args)
def test_retrieve_pool_stats(self):
"""lb-pool-stats test_id."""
resource = 'pool'
cmd = pool.RetrievePoolStats(test_cli20.MyApp(sys.stdout), None)
my_id = self.test_id
fields = ['bytes_in', 'bytes_out']
args = ['--fields', 'bytes_in', '--fields', 'bytes_out', my_id]
self.mox.StubOutWithMock(cmd, "get_client")
self.mox.StubOutWithMock(self.client.httpclient, "request")
cmd.get_client().MultipleTimes().AndReturn(self.client)
query = "&".join(["fields=%s" % field for field in fields])
expected_res = {'stats': {'bytes_in': '1234', 'bytes_out': '4321'}}
resstr = self.client.serialize(expected_res)
path = getattr(self.client, "pool_path_stats")
return_tup = (test_cli20.MyResp(200), resstr)
self.client.httpclient.request(
test_cli20.end_url(path % my_id, query), 'GET',
body=None,
headers=mox.ContainsKeyValue(
'X-Auth-Token', test_cli20.TOKEN)).AndReturn(return_tup)
self.mox.ReplayAll()
cmd_parser = cmd.get_parser("test_" + resource)
parsed_args = cmd_parser.parse_args(args)
cmd.run(parsed_args)
self.mox.VerifyAll()
self.mox.UnsetStubs()
_str = self.fake_stdout.make_string()
self.assertTrue('bytes_in' in _str)
self.assertTrue('bytes_out' in _str)
class CLITestV20LbPoolXML(CLITestV20LbPoolJSON):
format = 'xml'

View File

@@ -1,214 +0,0 @@
# Copyright 2013 Mirantis Inc.
# 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.
#
# @author: Ilya Shakhat, Mirantis Inc.
#
# vim: tabstop=4 shiftwidth=4 softtabstop=4
import sys
from quantumclient.quantum.v2_0.lb import vip
from tests.unit import test_cli20
class CLITestV20LbVipJSON(test_cli20.CLITestV20Base):
def setUp(self):
super(CLITestV20LbVipJSON, self).setUp(plurals={'tags': 'tag'})
def test_create_vip_with_mandatory_params(self):
"""lb-vip-create with all mandatory params."""
resource = 'vip'
cmd = vip.CreateVip(test_cli20.MyApp(sys.stdout), None)
pool_id = 'my-pool-id'
name = 'my-name'
subnet_id = 'subnet-id'
protocol_port = '1000'
protocol = 'tcp'
tenant_id = 'my-tenant'
my_id = 'my-id'
args = ['--name', name,
'--protocol-port', protocol_port,
'--protocol', protocol,
'--subnet-id', subnet_id,
'--tenant-id', tenant_id,
pool_id]
position_names = ['pool_id', 'name', 'protocol_port', 'protocol',
'subnet_id', 'tenant_id']
position_values = [pool_id, name, protocol_port, protocol,
subnet_id, tenant_id]
self._test_create_resource(resource, cmd, name, my_id, args,
position_names, position_values,
admin_state_up=True)
def test_create_vip_with_all_params(self):
"""lb-vip-create with all params."""
resource = 'vip'
cmd = vip.CreateVip(test_cli20.MyApp(sys.stdout), None)
pool_id = 'my-pool-id'
name = 'my-name'
description = 'my-desc'
address = '10.0.0.2'
admin_state = False
connection_limit = '1000'
subnet_id = 'subnet-id'
protocol_port = '80'
protocol = 'tcp'
tenant_id = 'my-tenant'
my_id = 'my-id'
args = ['--name', name,
'--description', description,
'--address', address,
'--admin-state-down',
'--connection-limit', connection_limit,
'--protocol-port', protocol_port,
'--protocol', protocol,
'--subnet-id', subnet_id,
'--tenant-id', tenant_id,
pool_id]
position_names = ['pool_id', 'name', 'description', 'address',
'admin_state_up', 'connection_limit',
'protocol_port', 'protocol', 'subnet_id',
'tenant_id']
position_values = [pool_id, name, description, address,
admin_state, connection_limit, protocol_port,
protocol, subnet_id,
tenant_id]
self._test_create_resource(resource, cmd, name, my_id, args,
position_names, position_values)
def test_create_vip_with_session_persistence_params(self):
"""lb-vip-create with mandatory and session-persistence params."""
resource = 'vip'
cmd = vip.CreateVip(test_cli20.MyApp(sys.stdout), None)
pool_id = 'my-pool-id'
name = 'my-name'
subnet_id = 'subnet-id'
protocol_port = '1000'
protocol = 'tcp'
tenant_id = 'my-tenant'
my_id = 'my-id'
args = ['--name', name,
'--protocol-port', protocol_port,
'--protocol', protocol,
'--subnet-id', subnet_id,
'--tenant-id', tenant_id,
pool_id,
'--session-persistence', 'type=dict',
'type=cookie,cookie_name=pie',
'--optional-param', 'any']
position_names = ['pool_id', 'name', 'protocol_port', 'protocol',
'subnet_id', 'tenant_id', 'optional_param']
position_values = [pool_id, name, protocol_port, protocol,
subnet_id, tenant_id, 'any']
extra_body = {
'session_persistence': {
'type': 'cookie',
'cookie_name': 'pie',
},
}
self._test_create_resource(resource, cmd, name, my_id, args,
position_names, position_values,
admin_state_up=True, extra_body=extra_body)
def test_list_vips(self):
"""lb-vip-list."""
resources = "vips"
cmd = vip.ListVip(test_cli20.MyApp(sys.stdout), None)
self._test_list_resources(resources, cmd, True)
def test_list_vips_pagination(self):
"""lb-vip-list."""
resources = "vips"
cmd = vip.ListVip(test_cli20.MyApp(sys.stdout), None)
self._test_list_resources_with_pagination(resources, cmd)
def test_list_vips_sort(self):
"""lb-vip-list --sort-key name --sort-key id --sort-key asc
--sort-key desc
"""
resources = "vips"
cmd = vip.ListVip(test_cli20.MyApp(sys.stdout), None)
self._test_list_resources(resources, cmd,
sort_key=["name", "id"],
sort_dir=["asc", "desc"])
def test_list_vips_limit(self):
"""lb-vip-list -P."""
resources = "vips"
cmd = vip.ListVip(test_cli20.MyApp(sys.stdout), None)
self._test_list_resources(resources, cmd, page_size=1000)
def test_show_vip_id(self):
"""lb-vip-show test_id."""
resource = 'vip'
cmd = vip.ShowVip(test_cli20.MyApp(sys.stdout), None)
args = ['--fields', 'id', self.test_id]
self._test_show_resource(resource, cmd, self.test_id, args, ['id'])
def test_show_vip_id_name(self):
"""lb-vip-show."""
resource = 'vip'
cmd = vip.ShowVip(test_cli20.MyApp(sys.stdout), None)
args = ['--fields', 'id', '--fields', 'name', self.test_id]
self._test_show_resource(resource, cmd, self.test_id,
args, ['id', 'name'])
def test_update_vip(self):
"""lb-vip-update myid --name myname --tags a b."""
resource = 'vip'
cmd = vip.UpdateVip(test_cli20.MyApp(sys.stdout), None)
self._test_update_resource(resource, cmd, 'myid',
['myid', '--name', 'myname',
'--tags', 'a', 'b'],
{'name': 'myname', 'tags': ['a', 'b'], })
def test_update_vip_with_session_persistence(self):
resource = 'vip'
cmd = vip.UpdateVip(test_cli20.MyApp(sys.stdout), None)
body = {
'session_persistence': {
'type': 'source',
},
}
args = ['myid', '--session-persistence', 'type=dict',
'type=source']
self._test_update_resource(resource, cmd, 'myid', args, body)
def test_update_vip_with_session_persistence_and_name(self):
resource = 'vip'
cmd = vip.UpdateVip(test_cli20.MyApp(sys.stdout), None)
body = {
'name': 'newname',
'session_persistence': {
'type': 'cookie',
'cookie_name': 'pie',
},
}
args = ['myid', '--name', 'newname',
'--session-persistence', 'type=dict',
'type=cookie,cookie_name=pie']
self._test_update_resource(resource, cmd, 'myid', args, body)
def test_delete_vip(self):
"""lb-vip-delete my-id."""
resource = 'vip'
cmd = vip.DeleteVip(test_cli20.MyApp(sys.stdout), None)
my_id = 'my-id'
args = [my_id]
self._test_delete_resource(resource, cmd, my_id, args)
class CLITestV20LbVipXML(CLITestV20LbVipJSON):
format = 'xml'

View File

@@ -1,320 +0,0 @@
# Copyright 2012 NEC Corporation
# 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.
#
# vim: tabstop=4 shiftwidth=4 softtabstop=4
import copy
import httplib2
import json
import uuid
import mox
import testtools
from quantumclient import client
from quantumclient.common import exceptions
USERNAME = 'testuser'
TENANT_NAME = 'testtenant'
PASSWORD = 'password'
AUTH_URL = 'authurl'
ENDPOINT_URL = 'localurl'
TOKEN = 'tokentoken'
REGION = 'RegionTest'
KS_TOKEN_RESULT = {
'access': {
'token': {'id': TOKEN,
'expires': '2012-08-11T07:49:01Z',
'tenant': {'id': str(uuid.uuid1())}},
'user': {'id': str(uuid.uuid1())},
'serviceCatalog': [
{'endpoints_links': [],
'endpoints': [{'adminURL': ENDPOINT_URL,
'internalURL': ENDPOINT_URL,
'publicURL': ENDPOINT_URL,
'region': REGION}],
'type': 'network',
'name': 'Quantum Service'}
]
}
}
ENDPOINTS_RESULT = {
'endpoints': [{
'type': 'network',
'name': 'Quantum Service',
'region': REGION,
'adminURL': ENDPOINT_URL,
'internalURL': ENDPOINT_URL,
'publicURL': ENDPOINT_URL
}]
}
class CLITestAuthKeystone(testtools.TestCase):
def setUp(self):
"""Prepare the test environment."""
super(CLITestAuthKeystone, self).setUp()
self.mox = mox.Mox()
self.client = client.HTTPClient(username=USERNAME,
tenant_name=TENANT_NAME,
password=PASSWORD,
auth_url=AUTH_URL,
region_name=REGION)
self.addCleanup(self.mox.VerifyAll)
self.addCleanup(self.mox.UnsetStubs)
def test_get_token(self):
self.mox.StubOutWithMock(self.client, "request")
res200 = self.mox.CreateMock(httplib2.Response)
res200.status = 200
self.client.request(
AUTH_URL + '/tokens', 'POST',
body=mox.IsA(str), headers=mox.IsA(dict)
).AndReturn((res200, json.dumps(KS_TOKEN_RESULT)))
self.client.request(
mox.StrContains(ENDPOINT_URL + '/resource'), 'GET',
headers=mox.ContainsKeyValue('X-Auth-Token', TOKEN)
).AndReturn((res200, ''))
self.mox.ReplayAll()
self.client.do_request('/resource', 'GET')
self.assertEqual(self.client.endpoint_url, ENDPOINT_URL)
self.assertEqual(self.client.auth_token, TOKEN)
def test_refresh_token(self):
self.mox.StubOutWithMock(self.client, "request")
self.client.auth_token = TOKEN
self.client.endpoint_url = ENDPOINT_URL
res200 = self.mox.CreateMock(httplib2.Response)
res200.status = 200
res401 = self.mox.CreateMock(httplib2.Response)
res401.status = 401
# If a token is expired, quantum server retruns 401
self.client.request(
mox.StrContains(ENDPOINT_URL + '/resource'), 'GET',
headers=mox.ContainsKeyValue('X-Auth-Token', TOKEN)
).AndReturn((res401, ''))
self.client.request(
AUTH_URL + '/tokens', 'POST',
body=mox.IsA(str), headers=mox.IsA(dict)
).AndReturn((res200, json.dumps(KS_TOKEN_RESULT)))
self.client.request(
mox.StrContains(ENDPOINT_URL + '/resource'), 'GET',
headers=mox.ContainsKeyValue('X-Auth-Token', TOKEN)
).AndReturn((res200, ''))
self.mox.ReplayAll()
self.client.do_request('/resource', 'GET')
def test_get_endpoint_url(self):
self.mox.StubOutWithMock(self.client, "request")
self.client.auth_token = TOKEN
res200 = self.mox.CreateMock(httplib2.Response)
res200.status = 200
self.client.request(
mox.StrContains(AUTH_URL + '/tokens/%s/endpoints' % TOKEN), 'GET',
headers=mox.IsA(dict)
).AndReturn((res200, json.dumps(ENDPOINTS_RESULT)))
self.client.request(
mox.StrContains(ENDPOINT_URL + '/resource'), 'GET',
headers=mox.ContainsKeyValue('X-Auth-Token', TOKEN)
).AndReturn((res200, ''))
self.mox.ReplayAll()
self.client.do_request('/resource', 'GET')
def test_get_endpoint_url_other(self):
self.client = client.HTTPClient(
username=USERNAME, tenant_name=TENANT_NAME, password=PASSWORD,
auth_url=AUTH_URL, region_name=REGION, endpoint_type='otherURL')
self.mox.StubOutWithMock(self.client, "request")
self.client.auth_token = TOKEN
res200 = self.mox.CreateMock(httplib2.Response)
res200.status = 200
self.client.request(
mox.StrContains(AUTH_URL + '/tokens/%s/endpoints' % TOKEN), 'GET',
headers=mox.IsA(dict)
).AndReturn((res200, json.dumps(ENDPOINTS_RESULT)))
self.mox.ReplayAll()
self.assertRaises(exceptions.EndpointTypeNotFound,
self.client.do_request,
'/resource',
'GET')
def test_get_endpoint_url_failed(self):
self.mox.StubOutWithMock(self.client, "request")
self.client.auth_token = TOKEN
res200 = self.mox.CreateMock(httplib2.Response)
res200.status = 200
res401 = self.mox.CreateMock(httplib2.Response)
res401.status = 401
self.client.request(
mox.StrContains(AUTH_URL + '/tokens/%s/endpoints' % TOKEN), 'GET',
headers=mox.IsA(dict)
).AndReturn((res401, ''))
self.client.request(
AUTH_URL + '/tokens', 'POST',
body=mox.IsA(str), headers=mox.IsA(dict)
).AndReturn((res200, json.dumps(KS_TOKEN_RESULT)))
self.client.request(
mox.StrContains(ENDPOINT_URL + '/resource'), 'GET',
headers=mox.ContainsKeyValue('X-Auth-Token', TOKEN)
).AndReturn((res200, ''))
self.mox.ReplayAll()
self.client.do_request('/resource', 'GET')
def test_url_for(self):
resources = copy.deepcopy(KS_TOKEN_RESULT)
endpoints = resources['access']['serviceCatalog'][0]['endpoints'][0]
endpoints['publicURL'] = 'public'
endpoints['internalURL'] = 'internal'
endpoints['adminURL'] = 'admin'
catalog = client.ServiceCatalog(resources)
# endpoint_type not specified
url = catalog.url_for(attr='region',
filter_value=REGION)
self.assertEqual('public', url)
# endpoint type specified (3 cases)
url = catalog.url_for(attr='region',
filter_value=REGION,
endpoint_type='adminURL')
self.assertEqual('admin', url)
url = catalog.url_for(attr='region',
filter_value=REGION,
endpoint_type='publicURL')
self.assertEqual('public', url)
url = catalog.url_for(attr='region',
filter_value=REGION,
endpoint_type='internalURL')
self.assertEqual('internal', url)
# endpoint_type requested does not exist.
self.assertRaises(exceptions.EndpointTypeNotFound,
catalog.url_for,
attr='region',
filter_value=REGION,
endpoint_type='privateURL')
# Test scenario with url_for when the service catalog only has publicURL.
def test_url_for_only_public_url(self):
resources = copy.deepcopy(KS_TOKEN_RESULT)
catalog = client.ServiceCatalog(resources)
# Remove endpoints from the catalog.
endpoints = resources['access']['serviceCatalog'][0]['endpoints'][0]
del endpoints['internalURL']
del endpoints['adminURL']
endpoints['publicURL'] = 'public'
# Use publicURL when specified explicitly.
url = catalog.url_for(attr='region',
filter_value=REGION,
endpoint_type='publicURL')
self.assertEqual('public', url)
# Use publicURL when specified explicitly.
url = catalog.url_for(attr='region',
filter_value=REGION)
self.assertEqual('public', url)
# Test scenario with url_for when the service catalog only has adminURL.
def test_url_for_only_admin_url(self):
resources = copy.deepcopy(KS_TOKEN_RESULT)
catalog = client.ServiceCatalog(resources)
endpoints = resources['access']['serviceCatalog'][0]['endpoints'][0]
del endpoints['internalURL']
del endpoints['publicURL']
endpoints['adminURL'] = 'admin'
# Use publicURL when specified explicitly.
url = catalog.url_for(attr='region',
filter_value=REGION,
endpoint_type='adminURL')
self.assertEqual('admin', url)
# But not when nothing is specified.
self.assertRaises(exceptions.EndpointTypeNotFound,
catalog.url_for,
attr='region',
filter_value=REGION)
def test_endpoint_type(self):
resources = copy.deepcopy(KS_TOKEN_RESULT)
endpoints = resources['access']['serviceCatalog'][0]['endpoints'][0]
endpoints['internalURL'] = 'internal'
endpoints['adminURL'] = 'admin'
endpoints['publicURL'] = 'public'
# Test default behavior is to choose public.
self.client = client.HTTPClient(
username=USERNAME, tenant_name=TENANT_NAME, password=PASSWORD,
auth_url=AUTH_URL, region_name=REGION)
self.client._extract_service_catalog(resources)
self.assertEqual(self.client.endpoint_url, 'public')
# Test admin url
self.client = client.HTTPClient(
username=USERNAME, tenant_name=TENANT_NAME, password=PASSWORD,
auth_url=AUTH_URL, region_name=REGION, endpoint_type='adminURL')
self.client._extract_service_catalog(resources)
self.assertEqual(self.client.endpoint_url, 'admin')
# Test public url
self.client = client.HTTPClient(
username=USERNAME, tenant_name=TENANT_NAME, password=PASSWORD,
auth_url=AUTH_URL, region_name=REGION, endpoint_type='publicURL')
self.client._extract_service_catalog(resources)
self.assertEqual(self.client.endpoint_url, 'public')
# Test internal url
self.client = client.HTTPClient(
username=USERNAME, tenant_name=TENANT_NAME, password=PASSWORD,
auth_url=AUTH_URL, region_name=REGION, endpoint_type='internalURL')
self.client._extract_service_catalog(resources)
self.assertEqual(self.client.endpoint_url, 'internal')
# Test url that isn't found in the service catalog
self.client = client.HTTPClient(
username=USERNAME, tenant_name=TENANT_NAME, password=PASSWORD,
auth_url=AUTH_URL, region_name=REGION, endpoint_type='privateURL')
self.assertRaises(exceptions.EndpointTypeNotFound,
self.client._extract_service_catalog,
resources)

View File

@@ -1,99 +0,0 @@
# Copyright 2012 OpenStack LLC.
# 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.
#
# vim: tabstop=4 shiftwidth=4 softtabstop=4
import testtools
from quantumclient.common import exceptions
from quantumclient.quantum import v2_0 as quantumV20
class CLITestArgs(testtools.TestCase):
def test_empty(self):
_mydict = quantumV20.parse_args_to_dict([])
self.assertEqual({}, _mydict)
def test_default_bool(self):
_specs = ['--my_bool', '--arg1', 'value1']
_mydict = quantumV20.parse_args_to_dict(_specs)
self.assertTrue(_mydict['my_bool'])
def test_bool_true(self):
_specs = ['--my-bool', 'type=bool', 'true', '--arg1', 'value1']
_mydict = quantumV20.parse_args_to_dict(_specs)
self.assertTrue(_mydict['my_bool'])
def test_bool_false(self):
_specs = ['--my_bool', 'type=bool', 'false', '--arg1', 'value1']
_mydict = quantumV20.parse_args_to_dict(_specs)
self.assertFalse(_mydict['my_bool'])
def test_nargs(self):
_specs = ['--tag', 'x', 'y', '--arg1', 'value1']
_mydict = quantumV20.parse_args_to_dict(_specs)
self.assertTrue('x' in _mydict['tag'])
self.assertTrue('y' in _mydict['tag'])
def test_badarg(self):
_specs = ['--tag=t', 'x', 'y', '--arg1', 'value1']
self.assertRaises(exceptions.CommandError,
quantumV20.parse_args_to_dict, _specs)
def test_badarg_with_minus(self):
_specs = ['--arg1', 'value1', '-D']
self.assertRaises(exceptions.CommandError,
quantumV20.parse_args_to_dict, _specs)
def test_goodarg_with_minus_number(self):
_specs = ['--arg1', 'value1', '-1', '-1.0']
_mydict = quantumV20.parse_args_to_dict(_specs)
self.assertEqual(['value1', '-1', '-1.0'],
_mydict['arg1'])
def test_badarg_duplicate(self):
_specs = ['--tag=t', '--arg1', 'value1', '--arg1', 'value1']
self.assertRaises(exceptions.CommandError,
quantumV20.parse_args_to_dict, _specs)
def test_badarg_early_type_specification(self):
_specs = ['type=dict', 'key=value']
self.assertRaises(exceptions.CommandError,
quantumV20.parse_args_to_dict, _specs)
def test_arg(self):
_specs = ['--tag=t', '--arg1', 'value1']
self.assertEqual('value1',
quantumV20.parse_args_to_dict(_specs)['arg1'])
def test_dict_arg(self):
_specs = ['--tag=t', '--arg1', 'type=dict', 'key1=value1,key2=value2']
arg1 = quantumV20.parse_args_to_dict(_specs)['arg1']
self.assertEqual('value1', arg1['key1'])
self.assertEqual('value2', arg1['key2'])
def test_dict_arg_with_attribute_named_type(self):
_specs = ['--tag=t', '--arg1', 'type=dict', 'type=value1,key2=value2']
arg1 = quantumV20.parse_args_to_dict(_specs)['arg1']
self.assertEqual('value1', arg1['type'])
self.assertEqual('value2', arg1['key2'])
def test_list_of_dict_arg(self):
_specs = ['--tag=t', '--arg1', 'type=dict',
'list=true', 'key1=value1,key2=value2']
arg1 = quantumV20.parse_args_to_dict(_specs)['arg1']
self.assertEqual('value1', arg1[0]['key1'])
self.assertEqual('value2', arg1[0]['key2'])

View File

@@ -1,504 +0,0 @@
# Copyright 2012 OpenStack LLC.
# 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.
#
# vim: tabstop=4 shiftwidth=4 softtabstop=4
import urllib
import fixtures
import mox
import testtools
from quantumclient.common import constants
from quantumclient import shell
from quantumclient.v2_0 import client
API_VERSION = "2.0"
FORMAT = 'json'
TOKEN = 'testtoken'
ENDURL = 'localurl'
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 MyResp(object):
def __init__(self, status):
self.status = status
class MyApp(object):
def __init__(self, _stdout):
self.stdout = _stdout
def end_url(path, query=None, format=FORMAT):
_url_str = ENDURL + "/v" + API_VERSION + path + "." + format
return query and _url_str + "?" + query or _url_str
class MyUrlComparator(mox.Comparator):
def __init__(self, lhs, client):
self.lhs = lhs
self.client = client
def equals(self, rhs):
return str(self) == rhs
def __str__(self):
if self.client and self.client.format != FORMAT:
lhs_parts = self.lhs.split("?", 1)
if len(lhs_parts) == 2:
lhs = ("%s.%s?%s" % (lhs_parts[0][:-4],
self.client.format,
lhs_parts[1]))
else:
lhs = ("%s.%s" % (lhs_parts[0][:-4],
self.client.format))
return lhs
return self.lhs
def __repr__(self):
return str(self)
class MyComparator(mox.Comparator):
def __init__(self, lhs, client):
self.lhs = lhs
self.client = client
def _com_dict(self, lhs, rhs):
if len(lhs) != len(rhs):
return False
for key, value in lhs.iteritems():
if key not in rhs:
return False
rhs_value = rhs[key]
if not self._com(value, rhs_value):
return False
return True
def _com_list(self, lhs, rhs):
if len(lhs) != len(rhs):
return False
for lhs_value in lhs:
if lhs_value not in rhs:
return False
return True
def _com(self, lhs, rhs):
if lhs is None:
return rhs is None
if isinstance(lhs, dict):
if not isinstance(rhs, dict):
return False
return self._com_dict(lhs, rhs)
if isinstance(lhs, list):
if not isinstance(rhs, list):
return False
return self._com_list(lhs, rhs)
if isinstance(lhs, tuple):
if not isinstance(rhs, tuple):
return False
return self._com_list(lhs, rhs)
return lhs == rhs
def equals(self, rhs):
if self.client:
rhs = self.client.deserialize(rhs, 200)
return self._com(self.lhs, rhs)
def __repr__(self):
if self.client:
return self.client.serialize(self.lhs)
return str(self.lhs)
class CLITestV20Base(testtools.TestCase):
format = 'json'
test_id = 'aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa'
id_field = 'id'
def _find_resourceid(self, client, resource, name_or_id):
return name_or_id
def _get_attr_metadata(self):
return self.metadata
client.Client.EXTED_PLURALS.update(constants.PLURALS)
client.Client.EXTED_PLURALS.update({'tags': 'tag'})
return {'plurals': client.Client.EXTED_PLURALS,
'xmlns': constants.XML_NS_V20,
constants.EXT_NS: {'prefix': 'http://xxxx.yy.com'}}
def setUp(self, plurals={}):
"""Prepare the test environment."""
super(CLITestV20Base, self).setUp()
client.Client.EXTED_PLURALS.update(constants.PLURALS)
client.Client.EXTED_PLURALS.update(plurals)
self.metadata = {'plurals': client.Client.EXTED_PLURALS,
'xmlns': constants.XML_NS_V20,
constants.EXT_NS: {'prefix':
'http://xxxx.yy.com'}}
self.mox = mox.Mox()
self.endurl = ENDURL
self.fake_stdout = FakeStdout()
self.useFixture(fixtures.MonkeyPatch('sys.stdout', self.fake_stdout))
self.useFixture(fixtures.MonkeyPatch(
'quantumclient.quantum.v2_0.find_resourceid_by_name_or_id',
self._find_resourceid))
self.useFixture(fixtures.MonkeyPatch(
'quantumclient.v2_0.client.Client.get_attr_metadata',
self._get_attr_metadata))
self.client = client.Client(token=TOKEN, endpoint_url=self.endurl)
def _test_create_resource(self, resource, cmd,
name, myid, args,
position_names, position_values, tenant_id=None,
tags=None, admin_state_up=True, shared=False,
extra_body=None, **kwargs):
self.mox.StubOutWithMock(cmd, "get_client")
self.mox.StubOutWithMock(self.client.httpclient, "request")
cmd.get_client().MultipleTimes().AndReturn(self.client)
non_admin_status_resources = ['subnet', 'floatingip', 'security_group',
'security_group_rule', 'qos_queue',
'network_gateway']
if (resource in non_admin_status_resources):
body = {resource: {}, }
else:
body = {resource: {'admin_state_up': admin_state_up, }, }
if tenant_id:
body[resource].update({'tenant_id': tenant_id})
if tags:
body[resource].update({'tags': tags})
if shared:
body[resource].update({'shared': shared})
if extra_body:
body[resource].update(extra_body)
body[resource].update(kwargs)
for i in xrange(len(position_names)):
body[resource].update({position_names[i]: position_values[i]})
ress = {resource:
{self.id_field: myid}, }
if name:
ress[resource].update({'name': name})
self.client.format = self.format
resstr = self.client.serialize(ress)
# url method body
path = getattr(self.client, resource + "s_path")
self.client.httpclient.request(
end_url(path, format=self.format), 'POST',
body=MyComparator(body, self.client),
headers=mox.ContainsKeyValue(
'X-Auth-Token', TOKEN)).AndReturn((MyResp(200), resstr))
args.extend(['--request-format', self.format])
self.mox.ReplayAll()
cmd_parser = cmd.get_parser('create_' + resource)
shell.run_command(cmd, cmd_parser, args)
self.mox.VerifyAll()
self.mox.UnsetStubs()
_str = self.fake_stdout.make_string()
self.assertTrue(myid in _str)
if name:
self.assertTrue(name in _str)
def _test_list_columns(self, cmd, resources_collection,
resources_out, args=['-f', 'json']):
self.mox.StubOutWithMock(cmd, "get_client")
self.mox.StubOutWithMock(self.client.httpclient, "request")
cmd.get_client().MultipleTimes().AndReturn(self.client)
self.client.format = self.format
resstr = self.client.serialize(resources_out)
path = getattr(self.client, resources_collection + "_path")
self.client.httpclient.request(
end_url(path, format=self.format), 'GET',
body=None,
headers=mox.ContainsKeyValue(
'X-Auth-Token', TOKEN)).AndReturn((MyResp(200), resstr))
args.extend(['--request-format', self.format])
self.mox.ReplayAll()
cmd_parser = cmd.get_parser("list_" + resources_collection)
shell.run_command(cmd, cmd_parser, args)
self.mox.VerifyAll()
self.mox.UnsetStubs()
def _test_list_resources(self, resources, cmd, detail=False, tags=[],
fields_1=[], fields_2=[], page_size=None,
sort_key=[], sort_dir=[], response_contents=None):
self.mox.StubOutWithMock(cmd, "get_client")
self.mox.StubOutWithMock(self.client.httpclient, "request")
cmd.get_client().MultipleTimes().AndReturn(self.client)
if response_contents is None:
contents = [{self.id_field: 'myid1', },
{self.id_field: 'myid2', }, ]
else:
contents = response_contents
reses = {resources: contents}
self.client.format = self.format
resstr = self.client.serialize(reses)
# url method body
query = ""
args = detail and ['-D', ] or []
args.extend(['--request-format', self.format])
if fields_1:
for field in fields_1:
args.append('--fields')
args.append(field)
if tags:
args.append('--')
args.append("--tag")
for tag in tags:
args.append(tag)
if isinstance(tag, unicode):
tag = urllib.quote(tag.encode('utf-8'))
if query:
query += "&tag=" + tag
else:
query = "tag=" + tag
if (not tags) and fields_2:
args.append('--')
if fields_2:
args.append("--fields")
for field in fields_2:
args.append(field)
if detail:
query = query and query + '&verbose=True' or 'verbose=True'
fields_1.extend(fields_2)
for field in fields_1:
if query:
query += "&fields=" + field
else:
query = "fields=" + field
if page_size:
args.append("--page-size")
args.append(str(page_size))
if query:
query += "&limit=%s" % page_size
else:
query = "limit=%s" % page_size
if sort_key:
for key in sort_key:
args.append('--sort-key')
args.append(key)
if query:
query += '&'
query += 'sort_key=%s' % key
if sort_dir:
len_diff = len(sort_key) - len(sort_dir)
if len_diff > 0:
sort_dir += ['asc'] * len_diff
elif len_diff < 0:
sort_dir = sort_dir[:len(sort_key)]
for dir in sort_dir:
args.append('--sort-dir')
args.append(dir)
if query:
query += '&'
query += 'sort_dir=%s' % dir
path = getattr(self.client, resources + "_path")
self.client.httpclient.request(
MyUrlComparator(end_url(path, query, format=self.format),
self.client),
'GET',
body=None,
headers=mox.ContainsKeyValue(
'X-Auth-Token', TOKEN)).AndReturn((MyResp(200), resstr))
self.mox.ReplayAll()
cmd_parser = cmd.get_parser("list_" + resources)
shell.run_command(cmd, cmd_parser, args)
self.mox.VerifyAll()
self.mox.UnsetStubs()
_str = self.fake_stdout.make_string()
if response_contents is None:
self.assertTrue('myid1' in _str)
return _str
def _test_list_resources_with_pagination(self, resources, cmd):
self.mox.StubOutWithMock(cmd, "get_client")
self.mox.StubOutWithMock(self.client.httpclient, "request")
cmd.get_client().MultipleTimes().AndReturn(self.client)
path = getattr(self.client, resources + "_path")
fake_query = "marker=myid2&limit=2"
reses1 = {resources: [{'id': 'myid1', },
{'id': 'myid2', }],
'%s_links' % resources: [{'href': end_url(path, fake_query),
'rel': 'next'}]}
reses2 = {resources: [{'id': 'myid3', },
{'id': 'myid4', }]}
self.client.format = self.format
resstr1 = self.client.serialize(reses1)
resstr2 = self.client.serialize(reses2)
self.client.httpclient.request(
end_url(path, "", format=self.format), 'GET',
body=None,
headers=mox.ContainsKeyValue(
'X-Auth-Token', TOKEN)).AndReturn((MyResp(200), resstr1))
self.client.httpclient.request(
end_url(path, fake_query, format=self.format), 'GET',
body=None,
headers=mox.ContainsKeyValue(
'X-Auth-Token', TOKEN)).AndReturn((MyResp(200), resstr2))
self.mox.ReplayAll()
cmd_parser = cmd.get_parser("list_" + resources)
args = ['--request-format', self.format]
shell.run_command(cmd, cmd_parser, args)
#parsed_args = cmd_parser.parse_args("")
#cmd.run(parsed_args)
self.mox.VerifyAll()
self.mox.UnsetStubs()
def _test_update_resource(self, resource, cmd, myid, args, extrafields):
self.mox.StubOutWithMock(cmd, "get_client")
self.mox.StubOutWithMock(self.client.httpclient, "request")
cmd.get_client().MultipleTimes().AndReturn(self.client)
body = {resource: extrafields}
path = getattr(self.client, resource + "_path")
self.client.httpclient.request(
MyUrlComparator(end_url(path % myid, format=self.format),
self.client),
'PUT',
body=MyComparator(body, self.client),
headers=mox.ContainsKeyValue(
'X-Auth-Token', TOKEN)).AndReturn((MyResp(204), None))
args.extend(['--request-format', self.format])
self.mox.ReplayAll()
cmd_parser = cmd.get_parser("update_" + resource)
shell.run_command(cmd, cmd_parser, args)
self.mox.VerifyAll()
self.mox.UnsetStubs()
_str = self.fake_stdout.make_string()
self.assertTrue(myid in _str)
def _test_show_resource(self, resource, cmd, myid, args, fields=[]):
self.mox.StubOutWithMock(cmd, "get_client")
self.mox.StubOutWithMock(self.client.httpclient, "request")
cmd.get_client().MultipleTimes().AndReturn(self.client)
query = "&".join(["fields=%s" % field for field in fields])
expected_res = {resource:
{self.id_field: myid,
'name': 'myname', }, }
self.client.format = self.format
resstr = self.client.serialize(expected_res)
path = getattr(self.client, resource + "_path")
self.client.httpclient.request(
end_url(path % myid, query, format=self.format), 'GET',
body=None,
headers=mox.ContainsKeyValue(
'X-Auth-Token', TOKEN)).AndReturn((MyResp(200), resstr))
args.extend(['--request-format', self.format])
self.mox.ReplayAll()
cmd_parser = cmd.get_parser("show_" + resource)
shell.run_command(cmd, cmd_parser, args)
self.mox.VerifyAll()
self.mox.UnsetStubs()
_str = self.fake_stdout.make_string()
self.assertTrue(myid in _str)
self.assertTrue('myname' in _str)
def _test_delete_resource(self, resource, cmd, myid, args):
self.mox.StubOutWithMock(cmd, "get_client")
self.mox.StubOutWithMock(self.client.httpclient, "request")
cmd.get_client().MultipleTimes().AndReturn(self.client)
path = getattr(self.client, resource + "_path")
self.client.httpclient.request(
end_url(path % myid, format=self.format), 'DELETE',
body=None,
headers=mox.ContainsKeyValue(
'X-Auth-Token', TOKEN)).AndReturn((MyResp(204), None))
args.extend(['--request-format', self.format])
self.mox.ReplayAll()
cmd_parser = cmd.get_parser("delete_" + resource)
shell.run_command(cmd, cmd_parser, args)
self.mox.VerifyAll()
self.mox.UnsetStubs()
_str = self.fake_stdout.make_string()
self.assertTrue(myid in _str)
def _test_update_resource_action(self, resource, cmd, myid, action, args,
body, retval=None):
self.mox.StubOutWithMock(cmd, "get_client")
self.mox.StubOutWithMock(self.client.httpclient, "request")
cmd.get_client().MultipleTimes().AndReturn(self.client)
path = getattr(self.client, resource + "_path")
path_action = '%s/%s' % (myid, action)
self.client.httpclient.request(
end_url(path % path_action, format=self.format), 'PUT',
body=MyComparator(body, self.client),
headers=mox.ContainsKeyValue(
'X-Auth-Token', TOKEN)).AndReturn((MyResp(204), retval))
args.extend(['--request-format', self.format])
self.mox.ReplayAll()
cmd_parser = cmd.get_parser("delete_" + resource)
shell.run_command(cmd, cmd_parser, args)
self.mox.VerifyAll()
self.mox.UnsetStubs()
_str = self.fake_stdout.make_string()
self.assertTrue(myid in _str)
class ClientV2UnicodeTestJson(CLITestV20Base):
def test_do_request(self):
self.client.format = self.format
self.mox.StubOutWithMock(self.client.httpclient, "request")
unicode_text = u'\u7f51\u7edc'
# url with unicode
action = u'/test'
expected_action = action.encode('utf-8')
# query string with unicode
params = {'test': unicode_text}
expect_query = urllib.urlencode({'test':
unicode_text.encode('utf-8')})
# request body with unicode
body = params
expect_body = self.client.serialize(body)
# headers with unicode
self.client.httpclient.auth_token = unicode_text
expected_auth_token = unicode_text.encode('utf-8')
self.client.httpclient.request(
end_url(expected_action, query=expect_query, format=self.format),
'PUT', body=expect_body,
headers=mox.ContainsKeyValue(
'X-Auth-Token',
expected_auth_token)).AndReturn((MyResp(200), expect_body))
self.mox.ReplayAll()
res_body = self.client.do_request('PUT', action, body=body,
params=params)
self.mox.VerifyAll()
self.mox.UnsetStubs()
# test response with unicode
self.assertEqual(res_body, body)
class ClientV2UnicodeTestXML(ClientV2UnicodeTestJson):
format = 'xml'

View File

@@ -1,49 +0,0 @@
# vim: tabstop=4 shiftwidth=4 softtabstop=4
# Copyright 2013 NEC Corporation
# 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.
import sys
from quantumclient.quantum.v2_0.extension import ListExt
from quantumclient.quantum.v2_0.extension import ShowExt
from tests.unit.test_cli20 import CLITestV20Base
from tests.unit.test_cli20 import MyApp
class CLITestV20Extension(CLITestV20Base):
id_field = 'alias'
def test_list_extensions(self):
resources = 'extensions'
cmd = ListExt(MyApp(sys.stdout), None)
contents = [{'alias': 'ext1', 'name': 'name1', 'other': 'other1'},
{'alias': 'ext2', 'name': 'name2', 'other': 'other2'}]
ret = self._test_list_resources(resources, cmd,
response_contents=contents)
ret_words = set(ret.split())
# Check only the default columns are shown.
self.assertTrue('name' in ret_words)
self.assertTrue('alias' in ret_words)
self.assertFalse('other' in ret_words)
def test_show_extension(self):
# -F option does not work for ext-show at the moment, so -F option
# is not passed in the commandline args as other tests do.
resource = 'extension'
cmd = ShowExt(MyApp(sys.stdout), None)
args = [self.test_id]
ext_alias = self.test_id
self._test_show_resource(resource, cmd, ext_alias, args, fields=[])

View File

@@ -1,139 +0,0 @@
#!/usr/bin/env python
# vim: tabstop=4 shiftwidth=4 softtabstop=4
# Copyright 2012 Red Hat
# 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.
import sys
from quantumclient.quantum.v2_0 import floatingip as fip
from tests.unit import test_cli20
class CLITestV20FloatingIpsJSON(test_cli20.CLITestV20Base):
def test_create_floatingip(self):
"""Create floatingip: fip1."""
resource = 'floatingip'
cmd = fip.CreateFloatingIP(test_cli20.MyApp(sys.stdout), None)
name = 'fip1'
myid = 'myid'
args = [name]
position_names = ['floating_network_id']
position_values = [name]
self._test_create_resource(resource, cmd, name, myid, args,
position_names, position_values)
def test_create_floatingip_and_port(self):
"""Create floatingip: fip1."""
resource = 'floatingip'
cmd = fip.CreateFloatingIP(test_cli20.MyApp(sys.stdout), None)
name = 'fip1'
myid = 'myid'
pid = 'mypid'
args = [name, '--port_id', pid]
position_names = ['floating_network_id', 'port_id']
position_values = [name, pid]
self._test_create_resource(resource, cmd, name, myid, args,
position_names, position_values)
# Test dashed options
args = [name, '--port-id', pid]
position_names = ['floating_network_id', 'port_id']
self._test_create_resource(resource, cmd, name, myid, args,
position_names, position_values)
def test_create_floatingip_and_port_and_address(self):
"""Create floatingip: fip1 with a given port and address."""
resource = 'floatingip'
cmd = fip.CreateFloatingIP(test_cli20.MyApp(sys.stdout), None)
name = 'fip1'
myid = 'myid'
pid = 'mypid'
addr = '10.0.0.99'
args = [name, '--port_id', pid, '--fixed_ip_address', addr]
position_names = ['floating_network_id', 'port_id', 'fixed_ip_address']
position_values = [name, pid, addr]
self._test_create_resource(resource, cmd, name, myid, args,
position_names, position_values)
# Test dashed options
args = [name, '--port-id', pid, '--fixed-ip-address', addr]
position_names = ['floating_network_id', 'port_id', 'fixed_ip_address']
self._test_create_resource(resource, cmd, name, myid, args,
position_names, position_values)
def test_list_floatingips(self):
"""list floatingips: -D."""
resources = 'floatingips'
cmd = fip.ListFloatingIP(test_cli20.MyApp(sys.stdout), None)
self._test_list_resources(resources, cmd, True)
def test_list_floatingips_pagination(self):
resources = 'floatingips'
cmd = fip.ListFloatingIP(test_cli20.MyApp(sys.stdout), None)
self._test_list_resources_with_pagination(resources, cmd)
def test_list_floatingips_sort(self):
"""list floatingips: --sort-key name --sort-key id --sort-key asc
--sort-key desc
"""
resources = 'floatingips'
cmd = fip.ListFloatingIP(test_cli20.MyApp(sys.stdout), None)
self._test_list_resources(resources, cmd,
sort_key=["name", "id"],
sort_dir=["asc", "desc"])
def test_list_floatingips_limit(self):
"""list floatingips: -P."""
resources = 'floatingips'
cmd = fip.ListFloatingIP(test_cli20.MyApp(sys.stdout), None)
self._test_list_resources(resources, cmd, page_size=1000)
def test_delete_floatingip(self):
"""Delete floatingip: fip1."""
resource = 'floatingip'
cmd = fip.DeleteFloatingIP(test_cli20.MyApp(sys.stdout), None)
myid = 'myid'
args = [myid]
self._test_delete_resource(resource, cmd, myid, args)
def test_show_floatingip(self):
"""Show floatingip: --fields id."""
resource = 'floatingip'
cmd = fip.ShowFloatingIP(test_cli20.MyApp(sys.stdout), None)
args = ['--fields', 'id', self.test_id]
self._test_show_resource(resource, cmd, self.test_id,
args, ['id'])
def test_disassociate_ip(self):
"""Disassociate floating IP: myid."""
resource = 'floatingip'
cmd = fip.DisassociateFloatingIP(test_cli20.MyApp(sys.stdout), None)
args = ['myid']
self._test_update_resource(resource, cmd, 'myid',
args, {"port_id": None}
)
def test_associate_ip(self):
"""Associate floating IP: myid portid."""
resource = 'floatingip'
cmd = fip.AssociateFloatingIP(test_cli20.MyApp(sys.stdout), None)
args = ['myid', 'portid']
self._test_update_resource(resource, cmd, 'myid',
args, {"port_id": "portid"}
)
class CLITestV20FloatingIpsXML(CLITestV20FloatingIpsJSON):
format = 'xml'

View File

@@ -1,533 +0,0 @@
# 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.
#
# vim: tabstop=4 shiftwidth=4 softtabstop=4
import sys
import mox
from quantumclient.common import exceptions
from quantumclient.common import utils
from quantumclient.quantum.v2_0 import network
from quantumclient import shell
from tests.unit import test_cli20
class CLITestV20NetworkJSON(test_cli20.CLITestV20Base):
def setUp(self):
super(CLITestV20NetworkJSON, self).setUp(plurals={'tags': 'tag'})
def test_create_network(self):
"""Create net: myname."""
resource = 'network'
cmd = network.CreateNetwork(test_cli20.MyApp(sys.stdout), None)
name = 'myname'
myid = 'myid'
args = [name, ]
position_names = ['name', ]
position_values = [name, ]
self._test_create_resource(resource, cmd, name, myid, args,
position_names, position_values)
def test_create_network_with_unicode(self):
"""Create net: u'\u7f51\u7edc'."""
resource = 'network'
cmd = network.CreateNetwork(test_cli20.MyApp(sys.stdout), None)
name = u'\u7f51\u7edc'
myid = 'myid'
args = [name, ]
position_names = ['name', ]
position_values = [name, ]
self._test_create_resource(resource, cmd, name, myid, args,
position_names, position_values)
def test_create_network_tenant(self):
"""Create net: --tenant_id tenantid myname."""
resource = 'network'
cmd = network.CreateNetwork(test_cli20.MyApp(sys.stdout), None)
name = 'myname'
myid = 'myid'
args = ['--tenant_id', 'tenantid', name]
position_names = ['name', ]
position_values = [name, ]
self._test_create_resource(resource, cmd, name, myid, args,
position_names, position_values,
tenant_id='tenantid')
# Test dashed options
args = ['--tenant-id', 'tenantid', name]
self._test_create_resource(resource, cmd, name, myid, args,
position_names, position_values,
tenant_id='tenantid')
def test_create_network_tags(self):
"""Create net: myname --tags a b."""
resource = 'network'
cmd = network.CreateNetwork(test_cli20.MyApp(sys.stdout), None)
name = 'myname'
myid = 'myid'
args = [name, '--tags', 'a', 'b']
position_names = ['name', ]
position_values = [name, ]
self._test_create_resource(resource, cmd, name, myid, args,
position_names, position_values,
tags=['a', 'b'])
def test_create_network_state(self):
"""Create net: --admin_state_down myname."""
resource = 'network'
cmd = network.CreateNetwork(test_cli20.MyApp(sys.stdout), None)
name = 'myname'
myid = 'myid'
args = ['--admin_state_down', name, ]
position_names = ['name', ]
position_values = [name, ]
self._test_create_resource(resource, cmd, name, myid, args,
position_names, position_values,
admin_state_up=False)
# Test dashed options
args = ['--admin-state-down', name, ]
self._test_create_resource(resource, cmd, name, myid, args,
position_names, position_values,
admin_state_up=False)
def test_list_nets_empty_with_column(self):
resources = "networks"
cmd = network.ListNetwork(test_cli20.MyApp(sys.stdout), None)
self.mox.StubOutWithMock(cmd, "get_client")
self.mox.StubOutWithMock(self.client.httpclient, "request")
self.mox.StubOutWithMock(network.ListNetwork, "extend_list")
network.ListNetwork.extend_list(mox.IsA(list), mox.IgnoreArg())
cmd.get_client().MultipleTimes().AndReturn(self.client)
reses = {resources: []}
resstr = self.client.serialize(reses)
# url method body
query = "id=myfakeid"
args = ['-c', 'id', '--', '--id', 'myfakeid']
path = getattr(self.client, resources + "_path")
self.client.httpclient.request(
test_cli20.end_url(path, query), 'GET',
body=None,
headers=mox.ContainsKeyValue(
'X-Auth-Token',
test_cli20.TOKEN)).AndReturn(
(test_cli20.MyResp(200), resstr))
self.mox.ReplayAll()
cmd_parser = cmd.get_parser("list_" + resources)
shell.run_command(cmd, cmd_parser, args)
self.mox.VerifyAll()
self.mox.UnsetStubs()
_str = self.fake_stdout.make_string()
self.assertEquals('\n', _str)
def _test_list_networks(self, cmd, detail=False, tags=[],
fields_1=[], fields_2=[], page_size=None,
sort_key=[], sort_dir=[]):
resources = "networks"
self.mox.StubOutWithMock(network.ListNetwork, "extend_list")
network.ListNetwork.extend_list(mox.IsA(list), mox.IgnoreArg())
self._test_list_resources(resources, cmd, detail, tags,
fields_1, fields_2, page_size=page_size,
sort_key=sort_key, sort_dir=sort_dir)
def test_list_nets_pagination(self):
cmd = network.ListNetwork(test_cli20.MyApp(sys.stdout), None)
self.mox.StubOutWithMock(network.ListNetwork, "extend_list")
network.ListNetwork.extend_list(mox.IsA(list), mox.IgnoreArg())
self._test_list_resources_with_pagination("networks", cmd)
def test_list_nets_sort(self):
"""list nets: --sort-key name --sort-key id --sort-dir asc
--sort-dir desc
"""
cmd = network.ListNetwork(test_cli20.MyApp(sys.stdout), None)
self._test_list_networks(cmd, sort_key=['name', 'id'],
sort_dir=['asc', 'desc'])
def test_list_nets_sort_with_keys_more_than_dirs(self):
"""list nets: --sort-key name --sort-key id --sort-dir desc
"""
cmd = network.ListNetwork(test_cli20.MyApp(sys.stdout), None)
self._test_list_networks(cmd, sort_key=['name', 'id'],
sort_dir=['desc'])
def test_list_nets_sort_with_dirs_more_than_keys(self):
"""list nets: --sort-key name --sort-dir desc --sort-dir asc
"""
cmd = network.ListNetwork(test_cli20.MyApp(sys.stdout), None)
self._test_list_networks(cmd, sort_key=['name'],
sort_dir=['desc', 'asc'])
def test_list_nets_limit(self):
"""list nets: -P."""
cmd = network.ListNetwork(test_cli20.MyApp(sys.stdout), None)
self._test_list_networks(cmd, page_size=1000)
def test_list_nets_detail(self):
"""list nets: -D."""
cmd = network.ListNetwork(test_cli20.MyApp(sys.stdout), None)
self._test_list_networks(cmd, True)
def test_list_nets_tags(self):
"""List nets: -- --tags a b."""
cmd = network.ListNetwork(test_cli20.MyApp(sys.stdout), None)
self._test_list_networks(cmd, tags=['a', 'b'])
def test_list_nets_tags_with_unicode(self):
"""List nets: -- --tags u'\u7f51\u7edc'."""
cmd = network.ListNetwork(test_cli20.MyApp(sys.stdout), None)
self._test_list_networks(cmd, tags=[u'\u7f51\u7edc'])
def test_list_nets_detail_tags(self):
"""List nets: -D -- --tags a b."""
cmd = network.ListNetwork(test_cli20.MyApp(sys.stdout), None)
self._test_list_networks(cmd, detail=True, tags=['a', 'b'])
def _test_list_nets_extend_subnets(self, data, expected):
def setup_list_stub(resources, data, query):
reses = {resources: data}
resstr = self.client.serialize(reses)
resp = (test_cli20.MyResp(200), resstr)
path = getattr(self.client, resources + '_path')
self.client.httpclient.request(
test_cli20.end_url(path, query), 'GET',
body=None,
headers=mox.ContainsKeyValue(
'X-Auth-Token', test_cli20.TOKEN)).AndReturn(resp)
cmd = network.ListNetwork(test_cli20.MyApp(sys.stdout), None)
self.mox.StubOutWithMock(cmd, 'get_client')
self.mox.StubOutWithMock(self.client.httpclient, 'request')
cmd.get_client().AndReturn(self.client)
setup_list_stub('networks', data, '')
cmd.get_client().AndReturn(self.client)
filters = ''
for n in data:
for s in n['subnets']:
filters = filters + "&id=%s" % s
setup_list_stub('subnets',
[{'id': 'mysubid1', 'cidr': '192.168.1.0/24'},
{'id': 'mysubid2', 'cidr': '172.16.0.0/24'},
{'id': 'mysubid3', 'cidr': '10.1.1.0/24'}],
query='fields=id&fields=cidr' + filters)
self.mox.ReplayAll()
args = []
cmd_parser = cmd.get_parser('list_networks')
parsed_args = cmd_parser.parse_args(args)
result = cmd.get_data(parsed_args)
self.mox.VerifyAll()
self.mox.UnsetStubs()
_result = [x for x in result[1]]
self.assertEqual(len(_result), len(expected))
for res, exp in zip(_result, expected):
self.assertEqual(len(res), len(exp))
for a, b in zip(res, exp):
self.assertEqual(a, b)
def test_list_nets_extend_subnets(self):
data = [{'id': 'netid1', 'name': 'net1', 'subnets': ['mysubid1']},
{'id': 'netid2', 'name': 'net2', 'subnets': ['mysubid2',
'mysubid3']}]
# id, name, subnets
expected = [('netid1', 'net1', 'mysubid1 192.168.1.0/24'),
('netid2', 'net2',
'mysubid2 172.16.0.0/24\nmysubid3 10.1.1.0/24')]
self._test_list_nets_extend_subnets(data, expected)
def test_list_nets_extend_subnets_no_subnet(self):
data = [{'id': 'netid1', 'name': 'net1', 'subnets': ['mysubid1']},
{'id': 'netid2', 'name': 'net2', 'subnets': ['mysubid4']}]
# id, name, subnets
expected = [('netid1', 'net1', 'mysubid1 192.168.1.0/24'),
('netid2', 'net2', 'mysubid4 ')]
self._test_list_nets_extend_subnets(data, expected)
def test_list_nets_fields(self):
"""List nets: --fields a --fields b -- --fields c d."""
cmd = network.ListNetwork(test_cli20.MyApp(sys.stdout), None)
self._test_list_networks(cmd,
fields_1=['a', 'b'], fields_2=['c', 'd'])
def _test_list_nets_columns(self, cmd, returned_body,
args=['-f', 'json']):
resources = 'networks'
self.mox.StubOutWithMock(network.ListNetwork, "extend_list")
network.ListNetwork.extend_list(mox.IsA(list), mox.IgnoreArg())
self._test_list_columns(cmd, resources, returned_body, args=args)
def test_list_nets_defined_column(self):
cmd = network.ListNetwork(test_cli20.MyApp(sys.stdout), None)
returned_body = {"networks": [{"name": "buildname3",
"id": "id3",
"tenant_id": "tenant_3",
"subnets": []}]}
self._test_list_nets_columns(cmd, returned_body,
args=['-f', 'json', '-c', 'id'])
_str = self.fake_stdout.make_string()
returned_networks = utils.loads(_str)
self.assertEquals(1, len(returned_networks))
net = returned_networks[0]
self.assertEquals(1, len(net))
self.assertEquals("id", net.keys()[0])
def test_list_nets_with_default_column(self):
cmd = network.ListNetwork(test_cli20.MyApp(sys.stdout), None)
returned_body = {"networks": [{"name": "buildname3",
"id": "id3",
"tenant_id": "tenant_3",
"subnets": []}]}
self._test_list_nets_columns(cmd, returned_body)
_str = self.fake_stdout.make_string()
returned_networks = utils.loads(_str)
self.assertEquals(1, len(returned_networks))
net = returned_networks[0]
self.assertEquals(3, len(net))
self.assertEquals(0, len(set(net) ^ set(cmd.list_columns)))
def test_list_external_nets_empty_with_column(self):
resources = "networks"
cmd = network.ListExternalNetwork(test_cli20.MyApp(sys.stdout), None)
self.mox.StubOutWithMock(cmd, "get_client")
self.mox.StubOutWithMock(self.client.httpclient, "request")
self.mox.StubOutWithMock(network.ListNetwork, "extend_list")
network.ListNetwork.extend_list(mox.IsA(list), mox.IgnoreArg())
cmd.get_client().MultipleTimes().AndReturn(self.client)
reses = {resources: []}
resstr = self.client.serialize(reses)
# url method body
query = "router%3Aexternal=True&id=myfakeid"
args = ['-c', 'id', '--', '--id', 'myfakeid']
path = getattr(self.client, resources + "_path")
self.client.httpclient.request(
test_cli20.end_url(path, query), 'GET',
body=None,
headers=mox.ContainsKeyValue(
'X-Auth-Token',
test_cli20.TOKEN)).AndReturn(
(test_cli20.MyResp(200), resstr))
self.mox.ReplayAll()
cmd_parser = cmd.get_parser("list_" + resources)
shell.run_command(cmd, cmd_parser, args)
self.mox.VerifyAll()
self.mox.UnsetStubs()
_str = self.fake_stdout.make_string()
self.assertEquals('\n', _str)
def _test_list_external_nets(self, resources, cmd,
detail=False, tags=[],
fields_1=[], fields_2=[]):
self.mox.StubOutWithMock(cmd, "get_client")
self.mox.StubOutWithMock(self.client.httpclient, "request")
self.mox.StubOutWithMock(network.ListNetwork, "extend_list")
network.ListNetwork.extend_list(mox.IsA(list), mox.IgnoreArg())
cmd.get_client().MultipleTimes().AndReturn(self.client)
reses = {resources: [{'id': 'myid1', },
{'id': 'myid2', }, ], }
resstr = self.client.serialize(reses)
# url method body
query = ""
args = detail and ['-D', ] or []
if fields_1:
for field in fields_1:
args.append('--fields')
args.append(field)
if tags:
args.append('--')
args.append("--tag")
for tag in tags:
args.append(tag)
if (not tags) and fields_2:
args.append('--')
if fields_2:
args.append("--fields")
for field in fields_2:
args.append(field)
fields_1.extend(fields_2)
for field in fields_1:
if query:
query += "&fields=" + field
else:
query = "fields=" + field
if query:
query += '&router%3Aexternal=True'
else:
query += 'router%3Aexternal=True'
for tag in tags:
if query:
query += "&tag=" + tag
else:
query = "tag=" + tag
if detail:
query = query and query + '&verbose=True' or 'verbose=True'
path = getattr(self.client, resources + "_path")
self.client.httpclient.request(
test_cli20.end_url(path, query), 'GET',
body=None,
headers=mox.ContainsKeyValue('X-Auth-Token', test_cli20.TOKEN)
).AndReturn((test_cli20.MyResp(200), resstr))
self.mox.ReplayAll()
cmd_parser = cmd.get_parser("list_" + resources)
shell.run_command(cmd, cmd_parser, args)
self.mox.VerifyAll()
self.mox.UnsetStubs()
_str = self.fake_stdout.make_string()
self.assertTrue('myid1' in _str)
def test_list_external_nets_detail(self):
"""list external nets: -D."""
resources = "networks"
cmd = network.ListExternalNetwork(test_cli20.MyApp(sys.stdout), None)
self._test_list_external_nets(resources, cmd, True)
def test_list_external_nets_tags(self):
"""List external nets: -- --tags a b."""
resources = "networks"
cmd = network.ListExternalNetwork(test_cli20.MyApp(sys.stdout), None)
self._test_list_external_nets(resources,
cmd, tags=['a', 'b'])
def test_list_external_nets_detail_tags(self):
"""List external nets: -D -- --tags a b."""
resources = "networks"
cmd = network.ListExternalNetwork(test_cli20.MyApp(sys.stdout), None)
self._test_list_external_nets(resources, cmd,
detail=True, tags=['a', 'b'])
def test_list_externel_nets_fields(self):
"""List external nets: --fields a --fields b -- --fields c d."""
resources = "networks"
cmd = network.ListExternalNetwork(test_cli20.MyApp(sys.stdout), None)
self._test_list_external_nets(resources, cmd,
fields_1=['a', 'b'],
fields_2=['c', 'd'])
def test_update_network_exception(self):
"""Update net: myid."""
resource = 'network'
cmd = network.UpdateNetwork(test_cli20.MyApp(sys.stdout), None)
self.assertRaises(exceptions.CommandError, self._test_update_resource,
resource, cmd, 'myid', ['myid'], {})
def test_update_network(self):
"""Update net: myid --name myname --tags a b."""
resource = 'network'
cmd = network.UpdateNetwork(test_cli20.MyApp(sys.stdout), None)
self._test_update_resource(resource, cmd, 'myid',
['myid', '--name', 'myname',
'--tags', 'a', 'b'],
{'name': 'myname', 'tags': ['a', 'b'], }
)
def test_update_network_with_unicode(self):
"""Update net: myid --name u'\u7f51\u7edc' --tags a b."""
resource = 'network'
cmd = network.UpdateNetwork(test_cli20.MyApp(sys.stdout), None)
self._test_update_resource(resource, cmd, 'myid',
['myid', '--name', u'\u7f51\u7edc',
'--tags', 'a', 'b'],
{'name': u'\u7f51\u7edc',
'tags': ['a', 'b'], }
)
def test_show_network(self):
"""Show net: --fields id --fields name myid."""
resource = 'network'
cmd = network.ShowNetwork(test_cli20.MyApp(sys.stdout), None)
args = ['--fields', 'id', '--fields', 'name', self.test_id]
self._test_show_resource(resource, cmd, self.test_id, args,
['id', 'name'])
def test_delete_network(self):
"""Delete net: myid."""
resource = 'network'
cmd = network.DeleteNetwork(test_cli20.MyApp(sys.stdout), None)
myid = 'myid'
args = [myid]
self._test_delete_resource(resource, cmd, myid, args)
def _test_extend_list(self, mox_calls):
data = [{'id': 'netid%d' % i, 'name': 'net%d' % i,
'subnets': ['mysubid%d' % i]}
for i in range(0, 10)]
self.mox.StubOutWithMock(self.client.httpclient, "request")
path = getattr(self.client, 'subnets_path')
cmd = network.ListNetwork(test_cli20.MyApp(sys.stdout), None)
self.mox.StubOutWithMock(cmd, "get_client")
cmd.get_client().MultipleTimes().AndReturn(self.client)
mox_calls(path, data)
self.mox.ReplayAll()
known_args, _vs = cmd.get_parser('create_subnets').parse_known_args()
cmd.extend_list(data, known_args)
self.mox.VerifyAll()
def _build_test_data(self, data):
subnet_ids = []
response = []
filters = ""
for n in data:
if 'subnets' in n:
subnet_ids.extend(n['subnets'])
for subnet_id in n['subnets']:
filters = "%s&id=%s" % (filters, subnet_id)
response.append({'id': subnet_id,
'cidr': '192.168.0.0/16'})
resp_str = self.client.serialize({'subnets': response})
resp = (test_cli20.MyResp(200), resp_str)
return filters, resp
def test_extend_list(self):
def mox_calls(path, data):
filters, response = self._build_test_data(data)
self.client.httpclient.request(
test_cli20.end_url(path, 'fields=id&fields=cidr' + filters),
'GET',
body=None,
headers=mox.ContainsKeyValue(
'X-Auth-Token', test_cli20.TOKEN)).AndReturn(response)
self._test_extend_list(mox_calls)
def test_extend_list_exceed_max_uri_len(self):
def mox_calls(path, data):
sub_data_lists = [data[:len(data) - 1], data[len(data) - 1:]]
filters, response = self._build_test_data(data)
# 1 char of extra URI len will cause a split in 2 requests
self.mox.StubOutWithMock(self.client, "_check_uri_length")
self.client._check_uri_length(mox.IgnoreArg()).AndRaise(
exceptions.RequestURITooLong(excess=1))
for data in sub_data_lists:
filters, response = self._build_test_data(data)
self.client._check_uri_length(mox.IgnoreArg()).AndReturn(None)
self.client.httpclient.request(
test_cli20.end_url(path,
'fields=id&fields=cidr%s' % filters),
'GET',
body=None,
headers=mox.ContainsKeyValue(
'X-Auth-Token', test_cli20.TOKEN)).AndReturn(response)
self._test_extend_list(mox_calls)
class CLITestV20NetworkXML(CLITestV20NetworkJSON):
format = 'xml'

View File

@@ -1,88 +0,0 @@
#!/usr/bin/env python
# vim: tabstop=4 shiftwidth=4 softtabstop=4
# Copyright 2013 Nicira Inc.
# 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.
import sys
from quantumclient.quantum.v2_0 import nvp_qos_queue as qos
from tests.unit import test_cli20
class CLITestV20NvpQosQueueJSON(test_cli20.CLITestV20Base):
def setUp(self):
super(CLITestV20NvpQosQueueJSON, self).setUp(
plurals={'qos_queues': 'qos_queue'})
def test_create_qos_queue(self):
"""Create a qos queue."""
resource = 'qos_queue'
cmd = qos.CreateQoSQueue(
test_cli20.MyApp(sys.stdout), None)
myid = 'myid'
name = 'my_queue'
default = False
args = ['--default', default, name]
position_names = ['name', 'default']
position_values = [name, default]
self._test_create_resource(resource, cmd, name, myid, args,
position_names, position_values)
def test_create_qos_queue_all_values(self):
"""Create a qos queue."""
resource = 'qos_queue'
cmd = qos.CreateQoSQueue(
test_cli20.MyApp(sys.stdout), None)
myid = 'myid'
name = 'my_queue'
default = False
min = '10'
max = '40'
qos_marking = 'untrusted'
dscp = '0'
args = ['--default', default, '--min', min, '--max', max,
'--qos-marking', qos_marking, '--dscp', dscp, name]
position_names = ['name', 'default', 'min', 'max', 'qos_marking',
'dscp']
position_values = [name, default, min, max, qos_marking, dscp]
self._test_create_resource(resource, cmd, name, myid, args,
position_names, position_values)
def test_list_qos_queue(self):
resources = "qos_queues"
cmd = qos.ListQoSQueue(
test_cli20.MyApp(sys.stdout), None)
self._test_list_resources(resources, cmd, True)
def test_show_qos_queue_id(self):
resource = 'qos_queue'
cmd = qos.ShowQoSQueue(
test_cli20.MyApp(sys.stdout), None)
args = ['--fields', 'id', self.test_id]
self._test_show_resource(resource, cmd, self.test_id,
args, ['id'])
def test_delete_qos_queue(self):
resource = 'qos_queue'
cmd = qos.DeleteQoSQueue(
test_cli20.MyApp(sys.stdout), None)
myid = 'myid'
args = [myid]
self._test_delete_resource(resource, cmd, myid, args)
class CLITestV20NvpQosQueueXML(CLITestV20NvpQosQueueJSON):
format = 'xml'

View File

@@ -1,114 +0,0 @@
# Copyright 2012 Nicira, Inc
# 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.
#
# vim: tabstop=4 shiftwidth=4 softtabstop=4
import sys
from quantumclient.quantum.v2_0 import nvpnetworkgateway as nwgw
from tests.unit import test_cli20
class CLITestV20NetworkGatewayJSON(test_cli20.CLITestV20Base):
resource = "network_gateway"
def setUp(self):
super(CLITestV20NetworkGatewayJSON, self).setUp(
plurals={'devices': 'device',
'network_gateways': 'network_gateway'})
def test_create_gateway(self):
cmd = nwgw.CreateNetworkGateway(test_cli20.MyApp(sys.stdout), None)
name = 'gw-test'
myid = 'myid'
args = [name, ]
position_names = ['name', ]
position_values = [name, ]
self._test_create_resource(self.resource, cmd, name, myid, args,
position_names, position_values)
def test_create_gateway_with_tenant(self):
cmd = nwgw.CreateNetworkGateway(test_cli20.MyApp(sys.stdout), None)
name = 'gw-test'
myid = 'myid'
args = ['--tenant_id', 'tenantid', name]
position_names = ['name', ]
position_values = [name, ]
self._test_create_resource(self.resource, cmd, name, myid, args,
position_names, position_values,
tenant_id='tenantid')
def test_create_gateway_with_device(self):
cmd = nwgw.CreateNetworkGateway(test_cli20.MyApp(sys.stdout), None)
name = 'gw-test'
myid = 'myid'
args = ['--device', 'device_id=test', name, ]
position_names = ['name', ]
position_values = [name, ]
self._test_create_resource(self.resource, cmd, name, myid, args,
position_names, position_values,
devices=[{'device_id': 'test'}])
def test_list_gateways(self):
resources = '%ss' % self.resource
cmd = nwgw.ListNetworkGateway(test_cli20.MyApp(sys.stdout), None)
self._test_list_resources(resources, cmd, True)
def test_update_gateway(self):
cmd = nwgw.UpdateNetworkGateway(test_cli20.MyApp(sys.stdout), None)
self._test_update_resource(self.resource, cmd, 'myid',
['myid', '--name', 'cavani'],
{'name': 'cavani'})
def test_delete_gateway(self):
cmd = nwgw.DeleteNetworkGateway(test_cli20.MyApp(sys.stdout), None)
myid = 'myid'
args = [myid]
self._test_delete_resource(self.resource, cmd, myid, args)
def test_show_gateway(self):
cmd = nwgw.ShowNetworkGateway(test_cli20.MyApp(sys.stdout), None)
args = ['--fields', 'id', '--fields', 'name', self.test_id]
self._test_show_resource(self.resource, cmd, self.test_id, args,
['id', 'name'])
def test_connect_network_to_gateway(self):
cmd = nwgw.ConnectNetworkGateway(test_cli20.MyApp(sys.stdout), None)
args = ['gw_id', 'net_id',
'--segmentation-type', 'edi',
'--segmentation-id', '7']
self._test_update_resource_action(self.resource, cmd, 'gw_id',
'connect_network',
args,
{'network_id': 'net_id',
'segmentation_type': 'edi',
'segmentation_id': '7'})
def test_disconnect_network_from_gateway(self):
cmd = nwgw.DisconnectNetworkGateway(test_cli20.MyApp(sys.stdout), None)
args = ['gw_id', 'net_id',
'--segmentation-type', 'edi',
'--segmentation-id', '7']
self._test_update_resource_action(self.resource, cmd, 'gw_id',
'disconnect_network',
args,
{'network_id': 'net_id',
'segmentation_type': 'edi',
'segmentation_id': '7'})
class CLITestV20NetworkGatewayXML(CLITestV20NetworkGatewayJSON):
format = 'xml'

View File

@@ -1,304 +0,0 @@
# Copyright 2012 OpenStack LLC.
# 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.
#
# vim: tabstop=4 shiftwidth=4 softtabstop=4
import sys
import mox
from quantumclient.quantum.v2_0 import port
from quantumclient import shell
from tests.unit import test_cli20
class CLITestV20PortJSON(test_cli20.CLITestV20Base):
def setUp(self):
super(CLITestV20PortJSON, self).setUp(plurals={'tags': 'tag'})
def test_create_port(self):
"""Create port: netid."""
resource = 'port'
cmd = port.CreatePort(test_cli20.MyApp(sys.stdout), None)
name = 'myname'
myid = 'myid'
netid = 'netid'
args = [netid]
position_names = ['network_id']
position_values = []
position_values.extend([netid])
self._test_create_resource(resource, cmd, name, myid, args,
position_names, position_values)
def test_create_port_full(self):
"""Create port: --mac_address mac --device_id deviceid netid."""
resource = 'port'
cmd = port.CreatePort(test_cli20.MyApp(sys.stdout), None)
name = 'myname'
myid = 'myid'
netid = 'netid'
args = ['--mac_address', 'mac', '--device_id', 'deviceid', netid]
position_names = ['network_id', 'mac_address', 'device_id']
position_values = [netid, 'mac', 'deviceid']
self._test_create_resource(resource, cmd, name, myid, args,
position_names, position_values)
# Test dashed options
args = ['--mac-address', 'mac', '--device-id', 'deviceid', netid]
position_names = ['network_id', 'mac_address', 'device_id']
self._test_create_resource(resource, cmd, name, myid, args,
position_names, position_values)
def test_create_port_tenant(self):
"""Create port: --tenant_id tenantid netid."""
resource = 'port'
cmd = port.CreatePort(test_cli20.MyApp(sys.stdout), None)
name = 'myname'
myid = 'myid'
netid = 'netid'
args = ['--tenant_id', 'tenantid', netid, ]
position_names = ['network_id']
position_values = []
position_values.extend([netid])
self._test_create_resource(resource, cmd, name, myid, args,
position_names, position_values,
tenant_id='tenantid')
# Test dashed options
args = ['--tenant-id', 'tenantid', netid, ]
self._test_create_resource(resource, cmd, name, myid, args,
position_names, position_values,
tenant_id='tenantid')
def test_create_port_tags(self):
"""Create port: netid mac_address device_id --tags a b."""
resource = 'port'
cmd = port.CreatePort(test_cli20.MyApp(sys.stdout), None)
name = 'myname'
myid = 'myid'
netid = 'netid'
args = [netid, '--tags', 'a', 'b']
position_names = ['network_id']
position_values = []
position_values.extend([netid])
self._test_create_resource(resource, cmd, name, myid, args,
position_names, position_values,
tags=['a', 'b'])
def test_create_port_secgroup(self):
"""Create port: --security-group sg1_id netid."""
resource = 'port'
cmd = port.CreatePort(test_cli20.MyApp(sys.stdout), None)
name = 'myname'
myid = 'myid'
netid = 'netid'
args = ['--security-group', 'sg1_id', netid]
position_names = ['network_id', 'security_groups']
position_values = [netid, ['sg1_id']]
self._test_create_resource(resource, cmd, name, myid, args,
position_names, position_values)
def test_create_port_secgroups(self):
"""Create port: <security_groups> netid
The <security_groups> are
--security-group sg1_id --security-group sg2_id
"""
resource = 'port'
cmd = port.CreatePort(test_cli20.MyApp(sys.stdout), None)
name = 'myname'
myid = 'myid'
netid = 'netid'
args = ['--security-group', 'sg1_id',
'--security-group', 'sg2_id',
netid]
position_names = ['network_id', 'security_groups']
position_values = [netid, ['sg1_id', 'sg2_id']]
self._test_create_resource(resource, cmd, name, myid, args,
position_names, position_values)
def test_list_ports(self):
"""List ports: -D."""
resources = "ports"
cmd = port.ListPort(test_cli20.MyApp(sys.stdout), None)
self._test_list_resources(resources, cmd, True)
def test_list_ports_pagination(self):
resources = "ports"
cmd = port.ListPort(test_cli20.MyApp(sys.stdout), None)
self._test_list_resources_with_pagination(resources, cmd)
def test_list_ports_sort(self):
"""list ports: --sort-key name --sort-key id --sort-key asc
--sort-key desc
"""
resources = "ports"
cmd = port.ListPort(test_cli20.MyApp(sys.stdout), None)
self._test_list_resources(resources, cmd,
sort_key=["name", "id"],
sort_dir=["asc", "desc"])
def test_list_ports_limit(self):
"""list ports: -P."""
resources = "ports"
cmd = port.ListPort(test_cli20.MyApp(sys.stdout), None)
self._test_list_resources(resources, cmd, page_size=1000)
def test_list_ports_tags(self):
"""List ports: -- --tags a b."""
resources = "ports"
cmd = port.ListPort(test_cli20.MyApp(sys.stdout), None)
self._test_list_resources(resources, cmd, tags=['a', 'b'])
def test_list_ports_detail_tags(self):
"""List ports: -D -- --tags a b."""
resources = "ports"
cmd = port.ListPort(test_cli20.MyApp(sys.stdout), None)
self._test_list_resources(resources, cmd, detail=True, tags=['a', 'b'])
def test_list_ports_fields(self):
"""List ports: --fields a --fields b -- --fields c d."""
resources = "ports"
cmd = port.ListPort(test_cli20.MyApp(sys.stdout), None)
self._test_list_resources(resources, cmd,
fields_1=['a', 'b'], fields_2=['c', 'd'])
def _test_list_router_port(self, resources, cmd,
myid, detail=False, tags=[],
fields_1=[], fields_2=[]):
self.mox.StubOutWithMock(cmd, "get_client")
self.mox.StubOutWithMock(self.client.httpclient, "request")
cmd.get_client().MultipleTimes().AndReturn(self.client)
reses = {resources: [{'id': 'myid1', },
{'id': 'myid2', }, ], }
resstr = self.client.serialize(reses)
# url method body
query = ""
args = detail and ['-D', ] or []
if fields_1:
for field in fields_1:
args.append('--fields')
args.append(field)
args.append(myid)
if tags:
args.append('--')
args.append("--tag")
for tag in tags:
args.append(tag)
if (not tags) and fields_2:
args.append('--')
if fields_2:
args.append("--fields")
for field in fields_2:
args.append(field)
fields_1.extend(fields_2)
for field in fields_1:
if query:
query += "&fields=" + field
else:
query = "fields=" + field
for tag in tags:
if query:
query += "&tag=" + tag
else:
query = "tag=" + tag
if detail:
query = query and query + '&verbose=True' or 'verbose=True'
query = query and query + '&device_id=%s' or 'device_id=%s'
path = getattr(self.client, resources + "_path")
self.client.httpclient.request(
test_cli20.end_url(path, query % myid), 'GET',
body=None,
headers=mox.ContainsKeyValue('X-Auth-Token', test_cli20.TOKEN)
).AndReturn((test_cli20.MyResp(200), resstr))
self.mox.ReplayAll()
cmd_parser = cmd.get_parser("list_" + resources)
shell.run_command(cmd, cmd_parser, args)
self.mox.VerifyAll()
self.mox.UnsetStubs()
_str = self.fake_stdout.make_string()
self.assertTrue('myid1' in _str)
def test_list_router_ports(self):
"""List router ports: -D."""
resources = "ports"
cmd = port.ListRouterPort(test_cli20.MyApp(sys.stdout), None)
self._test_list_router_port(resources, cmd,
self.test_id, True)
def test_list_router_ports_tags(self):
"""List router ports: -- --tags a b."""
resources = "ports"
cmd = port.ListRouterPort(test_cli20.MyApp(sys.stdout), None)
self._test_list_router_port(resources, cmd,
self.test_id, tags=['a', 'b'])
def test_list_router_ports_detail_tags(self):
"""List router ports: -D -- --tags a b."""
resources = "ports"
cmd = port.ListRouterPort(test_cli20.MyApp(sys.stdout), None)
self._test_list_router_port(resources, cmd, self.test_id,
detail=True, tags=['a', 'b'])
def test_list_router_ports_fields(self):
"""List ports: --fields a --fields b -- --fields c d."""
resources = "ports"
cmd = port.ListRouterPort(test_cli20.MyApp(sys.stdout), None)
self._test_list_router_port(resources, cmd, self.test_id,
fields_1=['a', 'b'],
fields_2=['c', 'd'])
def test_update_port(self):
"""Update port: myid --name myname --tags a b."""
resource = 'port'
cmd = port.UpdatePort(test_cli20.MyApp(sys.stdout), None)
self._test_update_resource(resource, cmd, 'myid',
['myid', '--name', 'myname',
'--tags', 'a', 'b'],
{'name': 'myname', 'tags': ['a', 'b'], }
)
def test_update_port_security_group_off(self):
"""Update port: --no-security-groups myid."""
resource = 'port'
cmd = port.UpdatePort(test_cli20.MyApp(sys.stdout), None)
self._test_update_resource(resource, cmd, 'myid',
['--no-security-groups', 'myid'],
{'security_groups': None})
def test_show_port(self):
"""Show port: --fields id --fields name myid."""
resource = 'port'
cmd = port.ShowPort(test_cli20.MyApp(sys.stdout), None)
args = ['--fields', 'id', '--fields', 'name', self.test_id]
self._test_show_resource(resource, cmd, self.test_id,
args, ['id', 'name'])
def test_delete_port(self):
"""Delete port: myid."""
resource = 'port'
cmd = port.DeletePort(test_cli20.MyApp(sys.stdout), None)
myid = 'myid'
args = [myid]
self._test_delete_resource(resource, cmd, myid, args)
class CLITestV20PortXML(CLITestV20PortJSON):
format = 'xml'

View File

@@ -1,197 +0,0 @@
# Copyright 2012 Nicira, Inc
# 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.
#
# vim: tabstop=4 shiftwidth=4 softtabstop=4
import sys
from quantumclient.common import exceptions
from quantumclient.quantum.v2_0 import router
from tests.unit import test_cli20
class CLITestV20RouterJSON(test_cli20.CLITestV20Base):
def test_create_router(self):
"""Create router: router1."""
resource = 'router'
cmd = router.CreateRouter(test_cli20.MyApp(sys.stdout), None)
name = 'router1'
myid = 'myid'
args = [name, ]
position_names = ['name', ]
position_values = [name, ]
self._test_create_resource(resource, cmd, name, myid, args,
position_names, position_values)
def test_create_router_tenant(self):
"""Create router: --tenant_id tenantid myname."""
resource = 'router'
cmd = router.CreateRouter(test_cli20.MyApp(sys.stdout), None)
name = 'myname'
myid = 'myid'
args = ['--tenant_id', 'tenantid', name]
position_names = ['name', ]
position_values = [name, ]
self._test_create_resource(resource, cmd, name, myid, args,
position_names, position_values,
tenant_id='tenantid')
def test_create_router_admin_state(self):
"""Create router: --admin_state_down myname."""
resource = 'router'
cmd = router.CreateRouter(test_cli20.MyApp(sys.stdout), None)
name = 'myname'
myid = 'myid'
args = ['--admin_state_down', name, ]
position_names = ['name', ]
position_values = [name, ]
self._test_create_resource(resource, cmd, name, myid, args,
position_names, position_values,
admin_state_up=False)
def test_list_routers_detail(self):
"""list routers: -D."""
resources = "routers"
cmd = router.ListRouter(test_cli20.MyApp(sys.stdout), None)
self._test_list_resources(resources, cmd, True)
def test_list_routers_pagination(self):
resources = "routers"
cmd = router.ListRouter(test_cli20.MyApp(sys.stdout), None)
self._test_list_resources_with_pagination(resources, cmd)
def test_list_routers_sort(self):
"""list routers: --sort-key name --sort-key id --sort-key asc
--sort-key desc
"""
resources = "routers"
cmd = router.ListRouter(test_cli20.MyApp(sys.stdout), None)
self._test_list_resources(resources, cmd,
sort_key=["name", "id"],
sort_dir=["asc", "desc"])
def test_list_routers_limit(self):
"""list routers: -P."""
resources = "routers"
cmd = router.ListRouter(test_cli20.MyApp(sys.stdout), None)
self._test_list_resources(resources, cmd, page_size=1000)
def test_update_router_exception(self):
"""Update router: myid."""
resource = 'router'
cmd = router.UpdateRouter(test_cli20.MyApp(sys.stdout), None)
self.assertRaises(exceptions.CommandError, self._test_update_resource,
resource, cmd, 'myid', ['myid'], {})
def test_update_router(self):
"""Update router: myid --name myname --tags a b."""
resource = 'router'
cmd = router.UpdateRouter(test_cli20.MyApp(sys.stdout), None)
self._test_update_resource(resource, cmd, 'myid',
['myid', '--name', 'myname'],
{'name': 'myname'}
)
def test_delete_router(self):
"""Delete router: myid."""
resource = 'router'
cmd = router.DeleteRouter(test_cli20.MyApp(sys.stdout), None)
myid = 'myid'
args = [myid]
self._test_delete_resource(resource, cmd, myid, args)
def test_show_router(self):
"""Show router: myid."""
resource = 'router'
cmd = router.ShowRouter(test_cli20.MyApp(sys.stdout), None)
args = ['--fields', 'id', '--fields', 'name', self.test_id]
self._test_show_resource(resource, cmd, self.test_id, args,
['id', 'name'])
def _test_add_remove_interface(self, action, mode, cmd, args):
resource = 'router'
subcmd = '%s_router_interface' % action
if mode == 'port':
body = {'port_id': 'portid'}
else:
body = {'subnet_id': 'subnetid'}
if action == 'add':
retval = {'subnet_id': 'subnetid', 'port_id': 'portid'}
else:
retval = None
self._test_update_resource_action(resource, cmd, 'myid',
subcmd, args,
body, retval)
def test_add_interface_compat(self):
"""Add interface to router: myid subnetid."""
cmd = router.AddInterfaceRouter(test_cli20.MyApp(sys.stdout), None)
args = ['myid', 'subnetid']
self._test_add_remove_interface('add', 'subnet', cmd, args)
def test_add_interface_by_subnet(self):
"""Add interface to router: myid subnet=subnetid."""
cmd = router.AddInterfaceRouter(test_cli20.MyApp(sys.stdout), None)
args = ['myid', 'subnet=subnetid']
self._test_add_remove_interface('add', 'subnet', cmd, args)
def test_add_interface_by_port(self):
"""Add interface to router: myid port=portid."""
cmd = router.AddInterfaceRouter(test_cli20.MyApp(sys.stdout), None)
args = ['myid', 'port=portid']
self._test_add_remove_interface('add', 'port', cmd, args)
def test_del_interface_compat(self):
"""Delete interface from router: myid subnetid."""
cmd = router.RemoveInterfaceRouter(test_cli20.MyApp(sys.stdout), None)
args = ['myid', 'subnetid']
self._test_add_remove_interface('remove', 'subnet', cmd, args)
def test_del_interface_by_subnet(self):
"""Delete interface from router: myid subnet=subnetid."""
cmd = router.RemoveInterfaceRouter(test_cli20.MyApp(sys.stdout), None)
args = ['myid', 'subnet=subnetid']
self._test_add_remove_interface('remove', 'subnet', cmd, args)
def test_del_interface_by_port(self):
"""Delete interface from router: myid port=portid."""
cmd = router.RemoveInterfaceRouter(test_cli20.MyApp(sys.stdout), None)
args = ['myid', 'port=portid']
self._test_add_remove_interface('remove', 'port', cmd, args)
def test_set_gateway(self):
"""Set external gateway for router: myid externalid."""
resource = 'router'
cmd = router.SetGatewayRouter(test_cli20.MyApp(sys.stdout), None)
args = ['myid', 'externalid']
self._test_update_resource(resource, cmd, 'myid',
args,
{"external_gateway_info":
{"network_id": "externalid",
"enable_snat": True}}
)
def test_remove_gateway(self):
"""Remove external gateway from router: externalid."""
resource = 'router'
cmd = router.RemoveGatewayRouter(test_cli20.MyApp(sys.stdout), None)
args = ['externalid']
self._test_update_resource(resource, cmd, 'externalid',
args, {"external_gateway_info": {}}
)
class CLITestV20RouterXML(CLITestV20RouterJSON):
format = 'xml'

View File

@@ -1,333 +0,0 @@
#!/usr/bin/env python
# vim: tabstop=4 shiftwidth=4 softtabstop=4
# Copyright 2012 Red Hat
# 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.
import sys
import mox
from quantumclient.quantum.v2_0 import securitygroup
from tests.unit import test_cli20
class CLITestV20SecurityGroupsJSON(test_cli20.CLITestV20Base):
def test_create_security_group(self):
"""Create security group: webservers."""
resource = 'security_group'
cmd = securitygroup.CreateSecurityGroup(
test_cli20.MyApp(sys.stdout), None)
name = 'webservers'
myid = 'myid'
args = [name, ]
position_names = ['name']
position_values = [name]
self._test_create_resource(resource, cmd, name, myid, args,
position_names, position_values)
def test_create_security_group_tenant(self):
"""Create security group: webservers."""
resource = 'security_group'
cmd = securitygroup.CreateSecurityGroup(
test_cli20.MyApp(sys.stdout), None)
name = 'webservers'
description = 'my webservers'
myid = 'myid'
args = ['--tenant_id', 'tenant_id', '--description', description, name]
position_names = ['name', 'description']
position_values = [name, description]
self._test_create_resource(resource, cmd, name, myid, args,
position_names, position_values,
tenant_id='tenant_id')
def test_create_security_group_with_description(self):
"""Create security group: webservers."""
resource = 'security_group'
cmd = securitygroup.CreateSecurityGroup(
test_cli20.MyApp(sys.stdout), None)
name = 'webservers'
description = 'my webservers'
myid = 'myid'
args = [name, '--description', description]
position_names = ['name', 'description']
position_values = [name, description]
self._test_create_resource(resource, cmd, name, myid, args,
position_names, position_values)
def test_list_security_groups(self):
resources = "security_groups"
cmd = securitygroup.ListSecurityGroup(
test_cli20.MyApp(sys.stdout), None)
self._test_list_resources(resources, cmd, True)
def test_list_security_groups_pagination(self):
resources = "security_groups"
cmd = securitygroup.ListSecurityGroup(
test_cli20.MyApp(sys.stdout), None)
self._test_list_resources_with_pagination(resources, cmd)
def test_list_security_groups_sort(self):
resources = "security_groups"
cmd = securitygroup.ListSecurityGroup(
test_cli20.MyApp(sys.stdout), None)
self._test_list_resources(resources, cmd,
sort_key=["name", "id"],
sort_dir=["asc", "desc"])
def test_list_security_groups_limit(self):
resources = "security_groups"
cmd = securitygroup.ListSecurityGroup(
test_cli20.MyApp(sys.stdout), None)
self._test_list_resources(resources, cmd, page_size=1000)
def test_show_security_group_id(self):
resource = 'security_group'
cmd = securitygroup.ShowSecurityGroup(
test_cli20.MyApp(sys.stdout), None)
args = ['--fields', 'id', self.test_id]
self._test_show_resource(resource, cmd, self.test_id,
args, ['id'])
def test_show_security_group_id_name(self):
resource = 'security_group'
cmd = securitygroup.ShowSecurityGroup(
test_cli20.MyApp(sys.stdout), None)
args = ['--fields', 'id', '--fields', 'name', self.test_id]
self._test_show_resource(resource, cmd, self.test_id,
args, ['id', 'name'])
def test_delete_security_group(self):
"""Delete security group: myid."""
resource = 'security_group'
cmd = securitygroup.DeleteSecurityGroup(
test_cli20.MyApp(sys.stdout), None)
myid = 'myid'
args = [myid]
self._test_delete_resource(resource, cmd, myid, args)
def test_update_security_group(self):
"""Update security group: myid --name myname --description desc."""
resource = 'security_group'
cmd = securitygroup.UpdateSecurityGroup(
test_cli20.MyApp(sys.stdout), None)
self._test_update_resource(resource, cmd, 'myid',
['myid', '--name', 'myname',
'--description', 'mydescription'],
{'name': 'myname',
'description': 'mydescription'}
)
def test_update_security_group_with_unicode(self):
resource = 'security_group'
cmd = securitygroup.UpdateSecurityGroup(
test_cli20.MyApp(sys.stdout), None)
self._test_update_resource(resource, cmd, 'myid',
['myid', '--name', u'\u7f51\u7edc',
'--description', u'\u7f51\u7edc'],
{'name': u'\u7f51\u7edc',
'description': u'\u7f51\u7edc'}
)
def test_create_security_group_rule_full(self):
"""Create security group rule."""
resource = 'security_group_rule'
cmd = securitygroup.CreateSecurityGroupRule(
test_cli20.MyApp(sys.stdout), None)
myid = 'myid'
direction = 'ingress'
ethertype = 'IPv4'
protocol = 'tcp'
port_range_min = '22'
port_range_max = '22'
remote_ip_prefix = '10.0.0.0/24'
security_group_id = '1'
remote_group_id = '1'
args = ['--remote_ip_prefix', remote_ip_prefix, '--direction',
direction, '--ethertype', ethertype, '--protocol', protocol,
'--port_range_min', port_range_min, '--port_range_max',
port_range_max, '--remote_group_id', remote_group_id,
security_group_id]
position_names = ['remote_ip_prefix', 'direction', 'ethertype',
'protocol', 'port_range_min', 'port_range_max',
'remote_group_id', 'security_group_id']
position_values = [remote_ip_prefix, direction, ethertype, protocol,
port_range_min, port_range_max, remote_group_id,
security_group_id]
self._test_create_resource(resource, cmd, None, myid, args,
position_names, position_values)
def test_delete_security_group_rule(self):
"""Delete security group rule: myid."""
resource = 'security_group_rule'
cmd = securitygroup.DeleteSecurityGroupRule(
test_cli20.MyApp(sys.stdout), None)
myid = 'myid'
args = [myid]
self._test_delete_resource(resource, cmd, myid, args)
def test_list_security_group_rules(self):
resources = "security_group_rules"
cmd = securitygroup.ListSecurityGroupRule(
test_cli20.MyApp(sys.stdout), None)
self.mox.StubOutWithMock(securitygroup.ListSecurityGroupRule,
"extend_list")
securitygroup.ListSecurityGroupRule.extend_list(mox.IsA(list),
mox.IgnoreArg())
self._test_list_resources(resources, cmd, True)
def test_list_security_group_rules_pagination(self):
resources = "security_group_rules"
cmd = securitygroup.ListSecurityGroupRule(
test_cli20.MyApp(sys.stdout), None)
self.mox.StubOutWithMock(securitygroup.ListSecurityGroupRule,
"extend_list")
securitygroup.ListSecurityGroupRule.extend_list(mox.IsA(list),
mox.IgnoreArg())
self._test_list_resources_with_pagination(resources, cmd)
def test_list_security_group_rules_sort(self):
resources = "security_group_rules"
cmd = securitygroup.ListSecurityGroupRule(
test_cli20.MyApp(sys.stdout), None)
self.mox.StubOutWithMock(securitygroup.ListSecurityGroupRule,
"extend_list")
securitygroup.ListSecurityGroupRule.extend_list(mox.IsA(list),
mox.IgnoreArg())
self._test_list_resources(resources, cmd,
sort_key=["name", "id"],
sort_dir=["asc", "desc"])
def test_list_security_group_rules_limit(self):
resources = "security_group_rules"
cmd = securitygroup.ListSecurityGroupRule(
test_cli20.MyApp(sys.stdout), None)
self.mox.StubOutWithMock(securitygroup.ListSecurityGroupRule,
"extend_list")
securitygroup.ListSecurityGroupRule.extend_list(mox.IsA(list),
mox.IgnoreArg())
self._test_list_resources(resources, cmd, page_size=1000)
def test_show_security_group_rule(self):
resource = 'security_group_rule'
cmd = securitygroup.ShowSecurityGroupRule(
test_cli20.MyApp(sys.stdout), None)
args = ['--fields', 'id', self.test_id]
self._test_show_resource(resource, cmd, self.test_id,
args, ['id'])
def _test_list_security_group_rules_extend(self, data=None, expected=None,
args=[], conv=True,
query_field=False):
def setup_list_stub(resources, data, query):
reses = {resources: data}
resstr = self.client.serialize(reses)
resp = (test_cli20.MyResp(200), resstr)
path = getattr(self.client, resources + '_path')
self.client.httpclient.request(
test_cli20.end_url(path, query), 'GET',
body=None,
headers=mox.ContainsKeyValue(
'X-Auth-Token', test_cli20.TOKEN)).AndReturn(resp)
# Setup the default data
_data = {'cols': ['id', 'security_group_id', 'remote_group_id'],
'data': [('ruleid1', 'myid1', 'myid1'),
('ruleid2', 'myid2', 'myid3'),
('ruleid3', 'myid2', 'myid2')]}
_expected = {'cols': ['id', 'security_group', 'remote_group'],
'data': [('ruleid1', 'group1', 'group1'),
('ruleid2', 'group2', 'group3'),
('ruleid3', 'group2', 'group2')]}
if data is None:
data = _data
list_data = [dict(zip(data['cols'], d)) for d in data['data']]
if expected is None:
expected = {}
expected['cols'] = expected.get('cols', _expected['cols'])
expected['data'] = expected.get('data', _expected['data'])
cmd = securitygroup.ListSecurityGroupRule(
test_cli20.MyApp(sys.stdout), None)
self.mox.StubOutWithMock(cmd, 'get_client')
self.mox.StubOutWithMock(self.client.httpclient, 'request')
cmd.get_client().AndReturn(self.client)
query = ''
if query_field:
query = '&'.join(['fields=' + f for f in data['cols']])
setup_list_stub('security_group_rules', list_data, query)
if conv:
cmd.get_client().AndReturn(self.client)
sec_ids = set()
for n in data['data']:
sec_ids.add(n[1])
sec_ids.add(n[2])
filters = ''
for id in sec_ids:
filters = filters + "&id=%s" % id
setup_list_stub('security_groups',
[{'id': 'myid1', 'name': 'group1'},
{'id': 'myid2', 'name': 'group2'},
{'id': 'myid3', 'name': 'group3'}],
query='fields=id&fields=name' + filters)
self.mox.ReplayAll()
cmd_parser = cmd.get_parser('list_security_group_rules')
parsed_args = cmd_parser.parse_args(args)
result = cmd.get_data(parsed_args)
self.mox.VerifyAll()
self.mox.UnsetStubs()
# Check columns
self.assertEqual(result[0], expected['cols'])
# Check data
_result = [x for x in result[1]]
self.assertEqual(len(_result), len(expected['data']))
for res, exp in zip(_result, expected['data']):
self.assertEqual(len(res), len(exp))
self.assertEqual(res, exp)
def test_list_security_group_rules_extend_source_id(self):
self._test_list_security_group_rules_extend()
def test_list_security_group_rules_extend_no_nameconv(self):
expected = {'cols': ['id', 'security_group_id', 'remote_group_id'],
'data': [('ruleid1', 'myid1', 'myid1'),
('ruleid2', 'myid2', 'myid3'),
('ruleid3', 'myid2', 'myid2')]}
args = ['--no-nameconv']
self._test_list_security_group_rules_extend(expected=expected,
args=args, conv=False)
def test_list_security_group_rules_extend_with_columns(self):
args = '-c id -c security_group_id -c remote_group_id'.split()
self._test_list_security_group_rules_extend(args=args)
def test_list_security_group_rules_extend_with_columns_no_id(self):
args = '-c id -c security_group -c remote_group'.split()
self._test_list_security_group_rules_extend(args=args)
def test_list_security_group_rules_extend_with_fields(self):
args = '-F id -F security_group_id -F remote_group_id'.split()
self._test_list_security_group_rules_extend(args=args,
query_field=True)
def test_list_security_group_rules_extend_with_fields_no_id(self):
args = '-F id -F security_group -F remote_group'.split()
self._test_list_security_group_rules_extend(args=args,
query_field=True)
class CLITestV20SecurityGroupsXML(CLITestV20SecurityGroupsJSON):
format = 'xml'

View File

@@ -1,402 +0,0 @@
# Copyright 2012 OpenStack LLC.
# 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.
#
# vim: tabstop=4 shiftwidth=4 softtabstop=4
import sys
from quantumclient.quantum.v2_0 import subnet
from tests.unit import test_cli20
class CLITestV20SubnetJSON(test_cli20.CLITestV20Base):
def setUp(self):
super(CLITestV20SubnetJSON, self).setUp(plurals={'tags': 'tag'})
def test_create_subnet(self):
"""Create subnet: --gateway gateway netid cidr."""
resource = 'subnet'
cmd = subnet.CreateSubnet(test_cli20.MyApp(sys.stdout), None)
name = 'myname'
myid = 'myid'
netid = 'netid'
cidr = 'cidrvalue'
gateway = 'gatewayvalue'
args = ['--gateway', gateway, netid, cidr]
position_names = ['ip_version', 'network_id', 'cidr', 'gateway_ip']
position_values = [4, netid, cidr, gateway]
self._test_create_resource(resource, cmd, name, myid, args,
position_names, position_values)
def test_create_subnet_with_no_gateway(self):
"""Create subnet: --no-gateway netid cidr."""
resource = 'subnet'
cmd = subnet.CreateSubnet(test_cli20.MyApp(sys.stdout), None)
name = 'myname'
myid = 'myid'
netid = 'netid'
cidr = 'cidrvalue'
args = ['--no-gateway', netid, cidr]
position_names = ['ip_version', 'network_id', 'cidr', 'gateway_ip']
position_values = [4, netid, cidr, None]
self._test_create_resource(resource, cmd, name, myid, args,
position_names, position_values)
def test_create_subnet_with_bad_gateway_option(self):
"""Create sbunet: --no-gateway netid cidr."""
resource = 'subnet'
cmd = subnet.CreateSubnet(test_cli20.MyApp(sys.stdout), None)
name = 'myname'
myid = 'myid'
netid = 'netid'
cidr = 'cidrvalue'
gateway = 'gatewayvalue'
args = ['--gateway', gateway, '--no-gateway', netid, cidr]
position_names = ['ip_version', 'network_id', 'cidr', 'gateway_ip']
position_values = [4, netid, cidr, None]
try:
self._test_create_resource(resource, cmd, name, myid, args,
position_names, position_values)
except Exception:
return
self.fail('No exception for bad gateway option')
def test_create_subnet_tenant(self):
"""Create subnet: --tenant_id tenantid netid cidr."""
resource = 'subnet'
cmd = subnet.CreateSubnet(test_cli20.MyApp(sys.stdout), None)
name = 'myname'
myid = 'myid'
netid = 'netid'
cidr = 'prefixvalue'
args = ['--tenant_id', 'tenantid', netid, cidr]
position_names = ['ip_version', 'network_id', 'cidr']
position_values = [4, netid, cidr]
self._test_create_resource(resource, cmd, name, myid, args,
position_names, position_values,
tenant_id='tenantid')
def test_create_subnet_tags(self):
"""Create subnet: netid cidr --tags a b."""
resource = 'subnet'
cmd = subnet.CreateSubnet(test_cli20.MyApp(sys.stdout), None)
name = 'myname'
myid = 'myid'
netid = 'netid'
cidr = 'prefixvalue'
args = [netid, cidr, '--tags', 'a', 'b']
position_names = ['ip_version', 'network_id', 'cidr']
position_values = [4, netid, cidr]
self._test_create_resource(resource, cmd, name, myid, args,
position_names, position_values,
tags=['a', 'b'])
def test_create_subnet_allocation_pool(self):
"""Create subnet: --tenant_id tenantid <allocation_pool> netid cidr.
The <allocation_pool> is --allocation_pool start=1.1.1.10,end=1.1.1.20
"""
resource = 'subnet'
cmd = subnet.CreateSubnet(test_cli20.MyApp(sys.stdout), None)
name = 'myname'
myid = 'myid'
netid = 'netid'
cidr = 'prefixvalue'
args = ['--tenant_id', 'tenantid',
'--allocation_pool', 'start=1.1.1.10,end=1.1.1.20',
netid, cidr]
position_names = ['ip_version', 'allocation_pools', 'network_id',
'cidr']
pool = [{'start': '1.1.1.10', 'end': '1.1.1.20'}]
position_values = [4, pool, netid, cidr]
self._test_create_resource(resource, cmd, name, myid, args,
position_names, position_values,
tenant_id='tenantid')
def test_create_subnet_allocation_pools(self):
"""Create subnet: --tenant-id tenantid <pools> netid cidr.
The <pools> are --allocation_pool start=1.1.1.10,end=1.1.1.20 and
--allocation_pool start=1.1.1.30,end=1.1.1.40
"""
resource = 'subnet'
cmd = subnet.CreateSubnet(test_cli20.MyApp(sys.stdout), None)
name = 'myname'
myid = 'myid'
netid = 'netid'
cidr = 'prefixvalue'
args = ['--tenant_id', 'tenantid',
'--allocation_pool', 'start=1.1.1.10,end=1.1.1.20',
'--allocation_pool', 'start=1.1.1.30,end=1.1.1.40',
netid, cidr]
position_names = ['ip_version', 'allocation_pools', 'network_id',
'cidr']
pools = [{'start': '1.1.1.10', 'end': '1.1.1.20'},
{'start': '1.1.1.30', 'end': '1.1.1.40'}]
position_values = [4, pools, netid, cidr]
self._test_create_resource(resource, cmd, name, myid, args,
position_names, position_values,
tenant_id='tenantid')
def test_create_subnet_host_route(self):
"""Create subnet: --tenant_id tenantid <host_route> netid cidr.
The <host_route> is
--host-route destination=172.16.1.0/24,nexthop=1.1.1.20
"""
resource = 'subnet'
cmd = subnet.CreateSubnet(test_cli20.MyApp(sys.stdout), None)
name = 'myname'
myid = 'myid'
netid = 'netid'
cidr = 'prefixvalue'
args = ['--tenant_id', 'tenantid',
'--host-route', 'destination=172.16.1.0/24,nexthop=1.1.1.20',
netid, cidr]
position_names = ['ip_version', 'host_routes', 'network_id',
'cidr']
route = [{'destination': '172.16.1.0/24', 'nexthop': '1.1.1.20'}]
position_values = [4, route, netid, cidr]
self._test_create_resource(resource, cmd, name, myid, args,
position_names, position_values,
tenant_id='tenantid')
def test_create_subnet_host_routes(self):
"""Create subnet: --tenant-id tenantid <host_routes> netid cidr.
The <host_routes> are
--host-route destination=172.16.1.0/24,nexthop=1.1.1.20 and
--host-route destination=172.17.7.0/24,nexthop=1.1.1.40
"""
resource = 'subnet'
cmd = subnet.CreateSubnet(test_cli20.MyApp(sys.stdout), None)
name = 'myname'
myid = 'myid'
netid = 'netid'
cidr = 'prefixvalue'
args = ['--tenant_id', 'tenantid',
'--host-route', 'destination=172.16.1.0/24,nexthop=1.1.1.20',
'--host-route', 'destination=172.17.7.0/24,nexthop=1.1.1.40',
netid, cidr]
position_names = ['ip_version', 'host_routes', 'network_id',
'cidr']
routes = [{'destination': '172.16.1.0/24', 'nexthop': '1.1.1.20'},
{'destination': '172.17.7.0/24', 'nexthop': '1.1.1.40'}]
position_values = [4, routes, netid, cidr]
self._test_create_resource(resource, cmd, name, myid, args,
position_names, position_values,
tenant_id='tenantid')
def test_create_subnet_dns_nameservers(self):
"""Create subnet: --tenant-id tenantid <dns-nameservers> netid cidr.
The <dns-nameservers> are
--dns-nameserver 1.1.1.20 and --dns-nameserver 1.1.1.40
"""
resource = 'subnet'
cmd = subnet.CreateSubnet(test_cli20.MyApp(sys.stdout), None)
name = 'myname'
myid = 'myid'
netid = 'netid'
cidr = 'prefixvalue'
args = ['--tenant_id', 'tenantid',
'--dns-nameserver', '1.1.1.20',
'--dns-nameserver', '1.1.1.40',
netid, cidr]
position_names = ['ip_version', 'dns_nameservers', 'network_id',
'cidr']
nameservers = ['1.1.1.20', '1.1.1.40']
position_values = [4, nameservers, netid, cidr]
self._test_create_resource(resource, cmd, name, myid, args,
position_names, position_values,
tenant_id='tenantid')
def test_create_subnet_with_disable_dhcp(self):
"""Create subnet: --tenant-id tenantid --disable-dhcp netid cidr."""
resource = 'subnet'
cmd = subnet.CreateSubnet(test_cli20.MyApp(sys.stdout), None)
name = 'myname'
myid = 'myid'
netid = 'netid'
cidr = 'prefixvalue'
args = ['--tenant_id', 'tenantid',
'--disable-dhcp',
netid, cidr]
position_names = ['ip_version', 'enable_dhcp', 'network_id',
'cidr']
position_values = [4, False, netid, cidr]
self._test_create_resource(resource, cmd, name, myid, args,
position_names, position_values,
tenant_id='tenantid')
def test_create_subnet_merge_single_plurar(self):
resource = 'subnet'
cmd = subnet.CreateSubnet(test_cli20.MyApp(sys.stdout), None)
name = 'myname'
myid = 'myid'
netid = 'netid'
cidr = 'prefixvalue'
args = ['--tenant_id', 'tenantid',
'--allocation-pool', 'start=1.1.1.10,end=1.1.1.20',
netid, cidr,
'--allocation-pools', 'list=true', 'type=dict',
'start=1.1.1.30,end=1.1.1.40']
position_names = ['ip_version', 'allocation_pools', 'network_id',
'cidr']
pools = [{'start': '1.1.1.10', 'end': '1.1.1.20'},
{'start': '1.1.1.30', 'end': '1.1.1.40'}]
position_values = [4, pools, netid, cidr]
self._test_create_resource(resource, cmd, name, myid, args,
position_names, position_values,
tenant_id='tenantid')
def test_create_subnet_merge_plurar(self):
resource = 'subnet'
cmd = subnet.CreateSubnet(test_cli20.MyApp(sys.stdout), None)
name = 'myname'
myid = 'myid'
netid = 'netid'
cidr = 'prefixvalue'
args = ['--tenant_id', 'tenantid',
netid, cidr,
'--allocation-pools', 'list=true', 'type=dict',
'start=1.1.1.30,end=1.1.1.40']
position_names = ['ip_version', 'allocation_pools', 'network_id',
'cidr']
pools = [{'start': '1.1.1.30', 'end': '1.1.1.40'}]
position_values = [4, pools, netid, cidr]
self._test_create_resource(resource, cmd, name, myid, args,
position_names, position_values,
tenant_id='tenantid')
def test_create_subnet_merge_single_single(self):
resource = 'subnet'
cmd = subnet.CreateSubnet(test_cli20.MyApp(sys.stdout), None)
name = 'myname'
myid = 'myid'
netid = 'netid'
cidr = 'prefixvalue'
args = ['--tenant_id', 'tenantid',
'--allocation-pool', 'start=1.1.1.10,end=1.1.1.20',
netid, cidr,
'--allocation-pool',
'start=1.1.1.30,end=1.1.1.40']
position_names = ['ip_version', 'allocation_pools', 'network_id',
'cidr']
pools = [{'start': '1.1.1.10', 'end': '1.1.1.20'},
{'start': '1.1.1.30', 'end': '1.1.1.40'}]
position_values = [4, pools, netid, cidr]
self._test_create_resource(resource, cmd, name, myid, args,
position_names, position_values,
tenant_id='tenantid')
def test_list_subnets_detail(self):
"""List subnets: -D."""
resources = "subnets"
cmd = subnet.ListSubnet(test_cli20.MyApp(sys.stdout), None)
self._test_list_resources(resources, cmd, True)
def test_list_subnets_tags(self):
"""List subnets: -- --tags a b."""
resources = "subnets"
cmd = subnet.ListSubnet(test_cli20.MyApp(sys.stdout), None)
self._test_list_resources(resources, cmd, tags=['a', 'b'])
def test_list_subnets_known_option_after_unknown(self):
"""List subnets: -- --tags a b --request-format xml."""
resources = "subnets"
cmd = subnet.ListSubnet(test_cli20.MyApp(sys.stdout), None)
self._test_list_resources(resources, cmd, tags=['a', 'b'])
def test_list_subnets_detail_tags(self):
"""List subnets: -D -- --tags a b."""
resources = "subnets"
cmd = subnet.ListSubnet(test_cli20.MyApp(sys.stdout), None)
self._test_list_resources(resources, cmd, detail=True, tags=['a', 'b'])
def test_list_subnets_fields(self):
"""List subnets: --fields a --fields b -- --fields c d."""
resources = "subnets"
cmd = subnet.ListSubnet(test_cli20.MyApp(sys.stdout), None)
self._test_list_resources(resources, cmd,
fields_1=['a', 'b'], fields_2=['c', 'd'])
def test_list_subnets_pagination(self):
resources = "subnets"
cmd = subnet.ListSubnet(test_cli20.MyApp(sys.stdout), None)
self._test_list_resources_with_pagination(resources, cmd)
def test_list_subnets_sort(self):
"""List subnets: --sort-key name --sort-key id --sort-key asc
--sort-key desc
"""
resources = "subnets"
cmd = subnet.ListSubnet(test_cli20.MyApp(sys.stdout), None)
self._test_list_resources(resources, cmd,
sort_key=["name", "id"],
sort_dir=["asc", "desc"])
def test_list_subnets_limit(self):
"""List subnets: -P."""
resources = "subnets"
cmd = subnet.ListSubnet(test_cli20.MyApp(sys.stdout), None)
self._test_list_resources(resources, cmd, page_size=1000)
def test_update_subnet(self):
"""Update subnet: myid --name myname --tags a b."""
resource = 'subnet'
cmd = subnet.UpdateSubnet(test_cli20.MyApp(sys.stdout), None)
self._test_update_resource(resource, cmd, 'myid',
['myid', '--name', 'myname',
'--tags', 'a', 'b'],
{'name': 'myname', 'tags': ['a', 'b'], }
)
def test_update_subnet_known_option_before_id(self):
"""Update subnet: --request-format json myid --name myname."""
# --request-format xml is known option
resource = 'subnet'
cmd = subnet.UpdateSubnet(test_cli20.MyApp(sys.stdout), None)
self._test_update_resource(resource, cmd, 'myid',
['--request-format', 'json',
'myid', '--name', 'myname'],
{'name': 'myname', }
)
def test_update_subnet_known_option_after_id(self):
"""Update subnet: myid --name myname --request-format json."""
# --request-format xml is known option
resource = 'subnet'
cmd = subnet.UpdateSubnet(test_cli20.MyApp(sys.stdout), None)
self._test_update_resource(resource, cmd, 'myid',
['myid', '--name', 'myname',
'--request-format', 'json'],
{'name': 'myname', }
)
def test_show_subnet(self):
"""Show subnet: --fields id --fields name myid."""
resource = 'subnet'
cmd = subnet.ShowSubnet(test_cli20.MyApp(sys.stdout), None)
args = ['--fields', 'id', '--fields', 'name', self.test_id]
self._test_show_resource(resource, cmd, self.test_id,
args, ['id', 'name'])
def test_delete_subnet(self):
"""Delete subnet: subnetid."""
resource = 'subnet'
cmd = subnet.DeleteSubnet(test_cli20.MyApp(sys.stdout), None)
myid = 'myid'
args = [myid]
self._test_delete_resource(resource, cmd, myid, args)
class CLITestV20SubnetXML(CLITestV20SubnetJSON):
format = 'xml'

View File

@@ -1,132 +0,0 @@
# Copyright 2012 OpenStack LLC.
# 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.
#
# vim: tabstop=4 shiftwidth=4 softtabstop=4
import uuid
import mox
import testtools
from quantumclient.common import exceptions
from quantumclient.quantum import v2_0 as quantumv20
from quantumclient.v2_0 import client
from tests.unit import test_cli20
class CLITestNameorID(testtools.TestCase):
def setUp(self):
"""Prepare the test environment."""
super(CLITestNameorID, self).setUp()
self.mox = mox.Mox()
self.endurl = test_cli20.ENDURL
self.client = client.Client(token=test_cli20.TOKEN,
endpoint_url=self.endurl)
self.addCleanup(self.mox.VerifyAll)
self.addCleanup(self.mox.UnsetStubs)
def test_get_id_from_id(self):
_id = str(uuid.uuid4())
reses = {'networks': [{'id': _id, }, ], }
resstr = self.client.serialize(reses)
self.mox.StubOutWithMock(self.client.httpclient, "request")
path = getattr(self.client, "networks_path")
self.client.httpclient.request(
test_cli20.end_url(path, "fields=id&id=" + _id), 'GET',
body=None,
headers=mox.ContainsKeyValue('X-Auth-Token', test_cli20.TOKEN)
).AndReturn((test_cli20.MyResp(200), resstr))
self.mox.ReplayAll()
returned_id = quantumv20.find_resourceid_by_name_or_id(
self.client, 'network', _id)
self.assertEqual(_id, returned_id)
def test_get_id_from_id_then_name_empty(self):
_id = str(uuid.uuid4())
reses = {'networks': [{'id': _id, }, ], }
resstr = self.client.serialize(reses)
resstr1 = self.client.serialize({'networks': []})
self.mox.StubOutWithMock(self.client.httpclient, "request")
path = getattr(self.client, "networks_path")
self.client.httpclient.request(
test_cli20.end_url(path, "fields=id&id=" + _id), 'GET',
body=None,
headers=mox.ContainsKeyValue('X-Auth-Token', test_cli20.TOKEN)
).AndReturn((test_cli20.MyResp(200), resstr1))
self.client.httpclient.request(
test_cli20.end_url(path, "fields=id&name=" + _id), 'GET',
body=None,
headers=mox.ContainsKeyValue('X-Auth-Token', test_cli20.TOKEN)
).AndReturn((test_cli20.MyResp(200), resstr))
self.mox.ReplayAll()
returned_id = quantumv20.find_resourceid_by_name_or_id(
self.client, 'network', _id)
self.assertEqual(_id, returned_id)
def test_get_id_from_name(self):
name = 'myname'
_id = str(uuid.uuid4())
reses = {'networks': [{'id': _id, }, ], }
resstr = self.client.serialize(reses)
self.mox.StubOutWithMock(self.client.httpclient, "request")
path = getattr(self.client, "networks_path")
self.client.httpclient.request(
test_cli20.end_url(path, "fields=id&name=" + name), 'GET',
body=None,
headers=mox.ContainsKeyValue('X-Auth-Token', test_cli20.TOKEN)
).AndReturn((test_cli20.MyResp(200), resstr))
self.mox.ReplayAll()
returned_id = quantumv20.find_resourceid_by_name_or_id(
self.client, 'network', name)
self.assertEqual(_id, returned_id)
def test_get_id_from_name_multiple(self):
name = 'myname'
reses = {'networks': [{'id': str(uuid.uuid4())},
{'id': str(uuid.uuid4())}]}
resstr = self.client.serialize(reses)
self.mox.StubOutWithMock(self.client.httpclient, "request")
path = getattr(self.client, "networks_path")
self.client.httpclient.request(
test_cli20.end_url(path, "fields=id&name=" + name), 'GET',
body=None,
headers=mox.ContainsKeyValue('X-Auth-Token', test_cli20.TOKEN)
).AndReturn((test_cli20.MyResp(200), resstr))
self.mox.ReplayAll()
try:
quantumv20.find_resourceid_by_name_or_id(
self.client, 'network', name)
except exceptions.QuantumClientException as ex:
self.assertTrue('Multiple' in ex.message)
def test_get_id_from_name_notfound(self):
name = 'myname'
reses = {'networks': []}
resstr = self.client.serialize(reses)
self.mox.StubOutWithMock(self.client.httpclient, "request")
path = getattr(self.client, "networks_path")
self.client.httpclient.request(
test_cli20.end_url(path, "fields=id&name=" + name), 'GET',
body=None,
headers=mox.ContainsKeyValue('X-Auth-Token', test_cli20.TOKEN)
).AndReturn((test_cli20.MyResp(200), resstr))
self.mox.ReplayAll()
try:
quantumv20.find_resourceid_by_name_or_id(
self.client, 'network', name)
except exceptions.QuantumClientException as ex:
self.assertTrue('Unable to find' in ex.message)
self.assertEqual(404, ex.status_code)

View File

@@ -1,45 +0,0 @@
#!/usr/bin/env python
# Copyright (C) 2013 Yahoo! Inc.
# 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.
# vim: tabstop=4 shiftwidth=4 softtabstop=4
import sys
from quantumclient.common import exceptions
from quantumclient.quantum.v2_0 import quota as test_quota
from tests.unit import test_cli20
class CLITestV20Quota(test_cli20.CLITestV20Base):
def test_show_quota(self):
resource = 'quota'
cmd = test_quota.ShowQuota(
test_cli20.MyApp(sys.stdout), None)
args = ['--tenant-id', self.test_id]
self._test_show_resource(resource, cmd, self.test_id, args)
def test_update_quota(self):
resource = 'quota'
cmd = test_quota.UpdateQuota(
test_cli20.MyApp(sys.stdout), None)
args = ['--tenant-id', self.test_id, '--network', 'test']
self.assertRaises(
exceptions.QuantumClientException, self._test_update_resource,
resource, cmd, self.test_id, args=args,
extrafields={'network': 'new'})
def test_delete_quota_get_parser(self):
cmd = test_cli20.MyApp(sys.stdout)
test_quota.DeleteQuota(cmd, None).get_parser(cmd)

View File

@@ -1,173 +0,0 @@
# Copyright (C) 2013 Yahoo! Inc.
# 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.
# vim: tabstop=4 shiftwidth=4 softtabstop=4
import argparse
import cStringIO
import os
import re
import sys
import fixtures
import mox
import testtools
from testtools import matchers
from quantumclient.common import exceptions
from quantumclient import shell as openstack_shell
DEFAULT_USERNAME = 'username'
DEFAULT_PASSWORD = 'password'
DEFAULT_TENANT_ID = 'tenant_id'
DEFAULT_TENANT_NAME = 'tenant_name'
DEFAULT_AUTH_URL = 'http://127.0.0.1:5000/v2.0/'
DEFAULT_TOKEN = '3bcc3d3a03f44e3d8377f9247b0ad155'
DEFAULT_URL = 'http://quantum.example.org:9696/'
class NoExitArgumentParser(argparse.ArgumentParser):
def error(self, message):
raise exceptions.CommandError(message)
class ShellTest(testtools.TestCase):
FAKE_ENV = {
'OS_USERNAME': DEFAULT_USERNAME,
'OS_PASSWORD': DEFAULT_PASSWORD,
'OS_TENANT_ID': DEFAULT_TENANT_ID,
'OS_TENANT_NAME': DEFAULT_TENANT_NAME,
'OS_AUTH_URL': DEFAULT_AUTH_URL}
def _tolerant_shell(self, cmd):
t_shell = openstack_shell.QuantumShell('2.0')
t_shell.run(cmd.split())
# Patch os.environ to avoid required auth info.
def setUp(self):
super(ShellTest, self).setUp()
self.mox = mox.Mox()
for var in self.FAKE_ENV:
self.useFixture(
fixtures.EnvironmentVariable(
var, self.FAKE_ENV[var]))
# Make a fake shell object, a helping wrapper to call it, and a quick
# way of asserting that certain API calls were made.
global shell, _shell, assert_called, assert_called_anytime
_shell = openstack_shell.QuantumShell('2.0')
shell = lambda cmd: _shell.run(cmd.split())
def shell(self, argstr):
orig = sys.stdout
clean_env = {}
_old_env, os.environ = os.environ, clean_env.copy()
try:
sys.stdout = cStringIO.StringIO()
_shell = openstack_shell.QuantumShell('2.0')
_shell.run(argstr.split())
except SystemExit:
exc_type, exc_value, exc_traceback = sys.exc_info()
self.assertEqual(exc_value.code, 0)
finally:
out = sys.stdout.getvalue()
sys.stdout.close()
sys.stdout = orig
os.environ = _old_env
return out
def test_run_unknown_command(self):
openstack_shell.QuantumShell('2.0').run('fake')
def test_help(self):
required = 'usage:'
help_text = self.shell('help')
self.assertThat(
help_text,
matchers.MatchesRegex(required))
def test_help_on_subcommand(self):
required = [
'.*?^usage: .* quota-list']
stdout = self.shell('help quota-list')
for r in required:
self.assertThat(
stdout,
matchers.MatchesRegex(r, re.DOTALL | re.MULTILINE))
def test_help_command(self):
required = 'usage:'
help_text = self.shell('help network-create')
self.assertThat(
help_text,
matchers.MatchesRegex(required))
def test_unknown_auth_strategy(self):
self.shell('--os-auth-strategy fake quota-list')
def test_auth(self):
self.shell(' --os-username test'
' --os-password test'
' --os-tenant-name test'
' --os-auth-url http://127.0.0.1:5000/'
' --os-auth-strategy keystone quota-list')
def test_build_option_parser(self):
quant_shell = openstack_shell.QuantumShell('2.0')
result = quant_shell.build_option_parser('descr', '2.0')
self.assertEqual(True, isinstance(result, argparse.ArgumentParser))
def test_main_with_unicode(self):
self.mox.StubOutClassWithMocks(openstack_shell, 'QuantumShell')
qshell_mock = openstack_shell.QuantumShell('2.0')
#self.mox.StubOutWithMock(qshell_mock, 'run')
unicode_text = u'\u7f51\u7edc'
argv = ['net-list', unicode_text, unicode_text.encode('utf-8')]
qshell_mock.run([u'net-list', unicode_text,
unicode_text]).AndReturn(0)
self.mox.ReplayAll()
ret = openstack_shell.main(argv=argv)
self.mox.VerifyAll()
self.mox.UnsetStubs()
self.assertEqual(ret, 0)
def test_endpoint_option(self):
shell = openstack_shell.QuantumShell('2.0')
parser = shell.build_option_parser('descr', '2.0')
# Neither $OS_ENDPOINT_TYPE nor --endpoint-type
namespace = parser.parse_args([])
self.assertEqual('publicURL', namespace.endpoint_type)
# --endpoint-type but not $OS_ENDPOINT_TYPE
namespace = parser.parse_args(['--endpoint-type=admin'])
self.assertEqual('admin', namespace.endpoint_type)
def test_endpoint_environment_variable(self):
fixture = fixtures.EnvironmentVariable("OS_ENDPOINT_TYPE",
"public")
self.useFixture(fixture)
shell = openstack_shell.QuantumShell('2.0')
parser = shell.build_option_parser('descr', '2.0')
# $OS_ENDPOINT_TYPE but not --endpoint-type
namespace = parser.parse_args([])
self.assertEqual("public", namespace.endpoint_type)
# --endpoint-type and $OS_ENDPOINT_TYPE
namespace = parser.parse_args(['--endpoint-type=admin'])
self.assertEqual('admin', namespace.endpoint_type)

View File

@@ -1,190 +0,0 @@
# Copyright (C) 2013 Yahoo! Inc.
# 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.
# vim: tabstop=4 shiftwidth=4 softtabstop=4
import datetime
import sys
import testtools
from quantumclient.common import exceptions
from quantumclient.common import utils
class TestUtils(testtools.TestCase):
def test_string_to_bool_true(self):
self.assertTrue(utils.str2bool('true'))
def test_string_to_bool_false(self):
self.assertFalse(utils.str2bool('false'))
def test_string_to_bool_None(self):
self.assertIsNone(utils.str2bool(None))
def test_string_to_dictionary(self):
input_str = 'key1=value1,key2=value2'
expected = {'key1': 'value1', 'key2': 'value2'}
self.assertEqual(expected, utils.str2dict(input_str))
def test_get_dict_item_properties(self):
item = {'name': 'test_name', 'id': 'test_id'}
fields = ('name', 'id')
actual = utils.get_item_properties(item=item, fields=fields)
self.assertEqual(('test_name', 'test_id'), actual)
def test_get_object_item_properties_mixed_case_fields(self):
class Fake(object):
def __init__(self):
self.id = 'test_id'
self.name = 'test_name'
self.test_user = 'test'
fields = ('name', 'id', 'test user')
mixed_fields = ('test user', 'ID')
item = Fake()
actual = utils.get_item_properties(item, fields, mixed_fields)
self.assertEqual(('test_name', 'test_id', 'test'), actual)
def test_get_object_item_desired_fields_differ_from_item(self):
class Fake(object):
def __init__(self):
self.id = 'test_id_1'
self.name = 'test_name'
self.test_user = 'test'
fields = ('name', 'id', 'test user')
item = Fake()
actual = utils.get_item_properties(item, fields)
self.assertNotEqual(('test_name', 'test_id', 'test'), actual)
def test_get_object_item_desired_fields_is_empty(self):
class Fake(object):
def __init__(self):
self.id = 'test_id_1'
self.name = 'test_name'
self.test_user = 'test'
fields = []
item = Fake()
actual = utils.get_item_properties(item, fields)
self.assertEqual((), actual)
def test_get_object_item_with_formatters(self):
class Fake(object):
def __init__(self):
self.id = 'test_id'
self.name = 'test_name'
self.test_user = 'test'
class FakeCallable(object):
def __call__(self, *args, **kwargs):
return 'pass'
fields = ('name', 'id', 'test user', 'is_public')
formatters = {'is_public': FakeCallable()}
item = Fake()
act = utils.get_item_properties(item, fields, formatters=formatters)
self.assertEqual(('test_name', 'test_id', 'test', 'pass'), act)
class JSONUtilsTestCase(testtools.TestCase):
def test_dumps(self):
self.assertEqual(utils.dumps({'a': 'b'}), '{"a": "b"}')
def test_dumps_dict_with_date_value(self):
x = datetime.datetime(1920, 2, 3, 4, 5, 6, 7)
res = utils.dumps({1: 'a', 2: x})
expected = '{"1": "a", "2": "1920-02-03 04:05:06.000007"}'
self.assertEqual(expected, res)
def test_dumps_dict_with_spaces(self):
x = datetime.datetime(1920, 2, 3, 4, 5, 6, 7)
res = utils.dumps({1: 'a ', 2: x})
expected = '{"1": "a ", "2": "1920-02-03 04:05:06.000007"}'
self.assertEqual(expected, res)
def test_loads(self):
self.assertEqual(utils.loads('{"a": "b"}'), {'a': 'b'})
class ToPrimitiveTestCase(testtools.TestCase):
def test_list(self):
self.assertEqual(utils.to_primitive([1, 2, 3]), [1, 2, 3])
def test_empty_list(self):
self.assertEqual(utils.to_primitive([]), [])
def test_tuple(self):
self.assertEqual(utils.to_primitive((1, 2, 3)), [1, 2, 3])
def test_empty_tuple(self):
self.assertEqual(utils.to_primitive(()), [])
def test_dict(self):
self.assertEqual(
utils.to_primitive(dict(a=1, b=2, c=3)),
dict(a=1, b=2, c=3))
def test_empty_dict(self):
self.assertEqual(utils.to_primitive({}), {})
def test_datetime(self):
x = datetime.datetime(1920, 2, 3, 4, 5, 6, 7)
self.assertEqual(
utils.to_primitive(x),
'1920-02-03 04:05:06.000007')
def test_iter(self):
x = xrange(1, 6)
self.assertEqual(utils.to_primitive(x), [1, 2, 3, 4, 5])
def test_iteritems(self):
d = {'a': 1, 'b': 2, 'c': 3}
class IterItemsClass(object):
def iteritems(self):
return d.iteritems()
x = IterItemsClass()
p = utils.to_primitive(x)
self.assertEqual(p, {'a': 1, 'b': 2, 'c': 3})
def test_nasties(self):
def foo():
pass
x = [datetime, foo, dir]
ret = utils.to_primitive(x)
self.assertEqual(len(ret), 3)
def test_to_primitive_dict_with_date_value(self):
x = datetime.datetime(1920, 2, 3, 4, 5, 6, 7)
res = utils.to_primitive({'a': x})
self.assertEqual({'a': '1920-02-03 04:05:06.000007'}, res)
class ImportClassTestCase(testtools.TestCase):
def test_import_class(self):
dt = utils.import_class('datetime.datetime')
self.assertTrue(sys.modules['datetime'].datetime is dt)
def test_import_bad_class(self):
self.assertRaises(
ImportError, utils.import_class,
'lol.u_mad.brah')
def test_get_client_class_invalid_version(self):
self.assertRaises(
exceptions.UnsupportedVersion,
utils.get_client_class, 'image', '2', {'image': '2'})