Remove quantumclient and proxy to neutronclient
Change-Id: I95a351071e68dfc2d67f3895c87f45ad2221767f
This commit is contained in:
@@ -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.
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
128
quantum_test.sh
128
quantum_test.sh
@@ -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
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
@@ -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)
|
||||
@@ -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'}
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
@@ -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
|
||||
|
||||
@@ -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"
|
||||
@@ -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)
|
||||
@@ -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__)
|
||||
@@ -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
|
||||
@@ -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))
|
||||
@@ -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)
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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)
|
||||
@@ -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
|
||||
@@ -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)
|
||||
@@ -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')
|
||||
@@ -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
|
||||
@@ -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')
|
||||
@@ -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'
|
||||
@@ -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
|
||||
@@ -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)
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
@@ -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)
|
||||
@@ -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
|
||||
@@ -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')
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
4
setup.py
4
setup.py
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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'
|
||||
@@ -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'
|
||||
@@ -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'
|
||||
@@ -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'
|
||||
@@ -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)
|
||||
@@ -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'])
|
||||
@@ -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'
|
||||
@@ -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=[])
|
||||
@@ -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'
|
||||
@@ -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'
|
||||
@@ -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'
|
||||
@@ -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'
|
||||
@@ -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'
|
||||
@@ -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'
|
||||
@@ -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'
|
||||
@@ -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'
|
||||
@@ -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)
|
||||
@@ -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)
|
||||
@@ -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)
|
||||
@@ -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'})
|
||||
Reference in New Issue
Block a user