Merge tag '4.1.1' into debian/mitaka
python-neutronclient 4.1.1 release meta:version: 4.1.1 meta:series: mitaka meta:release-type: release meta:announce: openstack-announce@lists.openstack.org meta:pypi: yes meta:first: no
This commit is contained in:
@@ -26,13 +26,13 @@ Transition to OpenStack Client
|
||||
This document details the transition roadmap for moving the neutron client's
|
||||
OpenStack Networking API support, both the Python library and the ``neutron``
|
||||
command-line interface (CLI), to the
|
||||
`OpenStack client (OSC) <https://github.com/openstack/python-openstackclient>`_
|
||||
`OpenStack Client (OSC) <https://github.com/openstack/python-openstackclient>`_
|
||||
and the `OpenStack Python SDK <https://github.com/openstack/python-openstacksdk>`_.
|
||||
This transition is being guided by the
|
||||
`Deprecate individual CLIs in favour of OSC <https://review.openstack.org/#/c/243348/>`_
|
||||
OpenStack spec. See the `Neutron RFE <https://bugs.launchpad.net/neutron/+bug/1521291>`_ and
|
||||
`OSC neutron-client blueprint <https://blueprints.launchpad.net/python-openstackclient/+spec/neutron-client>`_
|
||||
for the overall progress of this transition.
|
||||
OpenStack spec. See the `Neutron RFE <https://bugs.launchpad.net/neutron/+bug/1521291>`_,
|
||||
`OSC neutron support etherpad <https://etherpad.openstack.org/p/osc-neutron-support>`_ and
|
||||
details below for the overall progress of this transition.
|
||||
|
||||
Overview
|
||||
--------
|
||||
@@ -42,17 +42,15 @@ deprecated and then eventually removed. The ``neutron`` CLI will be replaced
|
||||
by OSC's networking support available via the ``openstack`` CLI. This is
|
||||
similar to the deprecation and removal process for the
|
||||
`keystone client's <https://github.com/openstack/python-keystoneclient>`_
|
||||
``keystone`` CLI.
|
||||
|
||||
The neutron client's Python library won't be deprecated. It will be available
|
||||
along side the networking support provided by the OpenStack Python SDK. However,
|
||||
the OpenStack Python SDK will be used to implement OSC's networking support.
|
||||
``keystone`` CLI. The neutron client's Python library won't be deprecated.
|
||||
It will be available along side the networking support provided by the
|
||||
OpenStack Python SDK.
|
||||
|
||||
Users of the neutron client's command extensions will need to transition to the
|
||||
`OSC plugin system <http://docs.openstack.org/developer/python-openstackclient/plugins.html>`_
|
||||
before the ``neutron`` CLI is removed. Such users will maintain their OSC plugin
|
||||
within their own project and will be responsible for deprecating and removing
|
||||
their ``neutron`` CLI extension.
|
||||
commands within their own project and will be responsible for deprecating and
|
||||
removing their ``neutron`` CLI extension.
|
||||
|
||||
Transition Steps
|
||||
----------------
|
||||
@@ -92,13 +90,12 @@ Transition Steps
|
||||
|
||||
* `Security Group Rule CRUD <https://bugs.launchpad.net/python-openstackclient/+bug/1519512>`_
|
||||
|
||||
6. **In Progress:** OSC enhances its networking support under the
|
||||
`neutron-client <https://blueprints.launchpad.net/python-openstackclient/+spec/neutron-client>`_
|
||||
OSC spec. At this point and when applicable, enhancements to the ``neutron``
|
||||
6. **In Progress:** OSC continues enhancing its networking support.
|
||||
At this point and when applicable, enhancements to the ``neutron``
|
||||
CLI must also be made to the ``openstack`` CLI and the OpenStack Python SDK.
|
||||
Enhancements to the networking support in the OpenStack Python SDK will be
|
||||
handled via bugs. Neutron stadium users of the neutron client's command
|
||||
extensions should start their transition to the OSC plugin system.
|
||||
handled via bugs. Users of the neutron client's command extensions should
|
||||
start their transition to the OSC plugin system.
|
||||
See the developer guide section below for more information on this step.
|
||||
|
||||
7. **Not Started:** Deprecate the ``neutron`` CLI once the criteria below have
|
||||
@@ -112,64 +109,116 @@ Transition Steps
|
||||
equivalent to the ``neutron`` CLI and it contains sufficient functional
|
||||
and unit test coverage.
|
||||
|
||||
* Neutron core and advanced services projects, Neutron documentation and
|
||||
`DevStack <http://docs.openstack.org/developer/devstack/>`_ use ``openstack``
|
||||
CLI instead of ``neutron`` CLI.
|
||||
* `Neutron Stadium <http://docs.openstack.org/developer/neutron/stadium/sub_projects.html>`_
|
||||
projects, Neutron documentation and `DevStack <http://docs.openstack.org/developer/devstack/>`_
|
||||
use ``openstack`` CLI instead of ``neutron`` CLI.
|
||||
|
||||
* Most neutron stadium users of the neutron client's command extensions have
|
||||
transitioned to the OSC plugin system and use the ``openstack`` CLI instead
|
||||
of the ``neutron`` CLI.
|
||||
* Most users of the neutron client's command extensions have transitioned
|
||||
to the OSC plugin system and use the ``openstack`` CLI instead of the
|
||||
``neutron`` CLI.
|
||||
|
||||
8. **Not Started:** Remove the ``neutron`` CLI after two deprecation cycles.
|
||||
|
||||
Developer Guide
|
||||
---------------
|
||||
The ``neutron`` CLI version 3.1.1, without extensions, supports over 200
|
||||
commands while the ``openstack`` CLI version 2.0.1 supports about 20
|
||||
networking commands. Of the 20 commands, most do not have all of the options
|
||||
The ``neutron`` CLI version 4.x, without extensions, supports over 200
|
||||
commands while the ``openstack`` CLI version 2.1.0 supports about 30
|
||||
networking commands. Of the 30 commands, many do not have all of the options
|
||||
or arguments of their ``neutron`` CLI equivalent. With this large functional
|
||||
gap, one critical question for developers during this transition is "Which
|
||||
CLI do I change?" The answer depends on the state of a command and the
|
||||
state of the overall transition. Details are outlined in the table
|
||||
below. Early stages of the transition will require dual maintenance.
|
||||
Eventually, dual maintenance will be reduced to critical bug fixes only
|
||||
with feature requests only being made to the ``openstack`` CLI.
|
||||
gap, a couple critical questions for developers during this transition are "Which
|
||||
CLI do I change?" and "Where does my CLI belong?" The answer depends on the
|
||||
state of a command and the state of the overall transition. Details are
|
||||
outlined in the tables below. Early stages of the transition will require dual
|
||||
maintenance. Eventually, dual maintenance will be reduced to critical bug fixes
|
||||
only with feature requests only being made to the ``openstack`` CLI.
|
||||
|
||||
+----------------------+------------------------+----------------------------------------------+
|
||||
| neutron Command | openstack Command | CLI to Change |
|
||||
+======================+========================+==============================================+
|
||||
| Exists | Doesn't Exist | neutron |
|
||||
+----------------------+------------------------+----------------------------------------------+
|
||||
| Exists | In Progress | neutron and update related OSC bug |
|
||||
+----------------------+------------------------+----------------------------------------------+
|
||||
| Exists | Exists | neutron and openstack |
|
||||
+----------------------+------------------------+----------------------------------------------+
|
||||
| Doesn't Exist | Doesn't Exist | neutron and openstack |
|
||||
+----------------------+------------------------+----------------------------------------------+
|
||||
| Doesn't Exist | Exists | openstack |
|
||||
+----------------------+------------------------+----------------------------------------------+
|
||||
**Which CLI do I change?**
|
||||
|
||||
When adding or updating an ``openstack`` networking command, changes may
|
||||
first be required to the OpenStack Python SDK to support the underlying
|
||||
networking resource object, properties and/or actions. Once the OpenStack
|
||||
Python SDK changes are merged, the related OSC changes can be merged.
|
||||
The OSC changes may require an update to the OSC openstacksdk version in the
|
||||
+----------------------+------------------------+-------------------------------------------------+
|
||||
| ``neutron`` Command | ``openstack`` Command | CLI to Change |
|
||||
+======================+========================+=================================================+
|
||||
| Exists | Doesn't Exist | ``neutron`` |
|
||||
+----------------------+------------------------+-------------------------------------------------+
|
||||
| Exists | In Progress | ``neutron`` and ``openstack`` |
|
||||
| | | (update related blueprint or bug) |
|
||||
+----------------------+------------------------+-------------------------------------------------+
|
||||
| Exists | Exists | ``openstack`` |
|
||||
| | | (assumes command parity resulting in |
|
||||
| | | ``neutron`` being deprecated) |
|
||||
+----------------------+------------------------+-------------------------------------------------+
|
||||
| Doesn't Exist | Doesn't Exist | ``openstack`` |
|
||||
+----------------------+------------------------+-------------------------------------------------+
|
||||
|
||||
**Where does my CLI belong?**
|
||||
|
||||
+---------------------------+-------------------+-------------------------------------------------+
|
||||
| Networking Commands | OSC Plugin | OpenStack Project for ``openstack`` Commands |
|
||||
+===========================+===================+=================================================+
|
||||
| Core (Stable) | No | python-openstackclient |
|
||||
+---------------------------+-------------------+-------------------------------------------------+
|
||||
| Core (New/Experimental) | Yes | python-neutronclient |
|
||||
| | | (with possible move to python-openstackclient) |
|
||||
+---------------------------+-------------------+-------------------------------------------------+
|
||||
| LBaaS v2 | Yes | neutron-lbaas |
|
||||
+---------------------------+-------------------+-------------------------------------------------+
|
||||
| VPNaaS v2 | Yes | neutron-vpnaas |
|
||||
+---------------------------+-------------------+-------------------------------------------------+
|
||||
| FWaaS v2 | Yes | neutron-fwaas |
|
||||
+---------------------------+-------------------+-------------------------------------------------+
|
||||
| LBaaS v1 | N/A | None (deprecated) |
|
||||
+---------------------------+-------------------+-------------------------------------------------+
|
||||
| FWaaS v1 | N/A | None (deprecated) |
|
||||
+---------------------------+-------------------+-------------------------------------------------+
|
||||
| Other | Yes | Applicable project owning networking resource |
|
||||
+---------------------------+-------------------+-------------------------------------------------+
|
||||
|
||||
|
||||
The following network resources are part of the "Core (Stable)" group:
|
||||
|
||||
- availability zone
|
||||
- extension
|
||||
- floating ip
|
||||
- network
|
||||
- port
|
||||
- quota
|
||||
- rbac
|
||||
- router
|
||||
- security group
|
||||
- security group rule
|
||||
- subnet
|
||||
- subnet pool
|
||||
|
||||
|
||||
When adding or updating an ``openstack`` networking command to
|
||||
python-openstackclient, changes may first be required to the
|
||||
OpenStack Python SDK to support the underlying networking resource object,
|
||||
properties and/or actions. Once the OpenStack Python SDK changes are merged,
|
||||
the related OSC changes can be merged. The OSC changes may require an update
|
||||
to the OSC openstacksdk version in the
|
||||
`requirements.txt <https://github.com/openstack/python-openstackclient/blob/master/requirements.txt>`_
|
||||
file.
|
||||
file. ``openstack`` networking commands outside python-openstackclient
|
||||
are encouraged but not required to use the OpenStack Python SDK.
|
||||
|
||||
Neutron stadium users of the neutron client's command extensions must adopt the
|
||||
When adding an ``openstack`` networking command to python-openstackclient,
|
||||
you can optionally propose an
|
||||
`OSC command spec <https://github.com/openstack/python-openstackclient/blob/master/doc/source/specs/commands.rst>`_
|
||||
which documents the new command interface before proceeding with the implementation.
|
||||
|
||||
Users of the neutron client's command extensions must adopt the
|
||||
`OSC plugin system <http://docs.openstack.org/developer/python-openstackclient/plugins.html>`_
|
||||
for this transition. Such users will maintain their OSC plugin within their
|
||||
own project and should follow the guidance in the table above to determine
|
||||
which CLI to change.
|
||||
which command to change.
|
||||
|
||||
Developer References
|
||||
--------------------
|
||||
|
||||
* See `OSC neutron-client blueprint <https://blueprints.launchpad.net/python-openstackclient/+spec/neutron-client>`_
|
||||
to determine if an ``openstack`` command is in progress. See the ``Related bugs`` list.
|
||||
* See `OSC neutron support etherpad <https://etherpad.openstack.org/p/osc-neutron-support>`_
|
||||
to determine if an ``openstack`` command is in progress.
|
||||
* See `OSC command list <http://docs.openstack.org/developer/python-openstackclient/command-list.html>`_
|
||||
to determine if an ``openstack`` command exists.
|
||||
* See `OSC command spec list <https://github.com/openstack/python-openstackclient/tree/master/doc/source/specs/command-objects>`_
|
||||
to determine if an ``openstack`` command spec exists.
|
||||
* See `OSC plugin command list <http://docs.openstack.org/developer/python-openstackclient/plugin-commands.html>`_
|
||||
to determine if an ``openstack`` plugin command exists.
|
||||
* See `OSC command structure <http://docs.openstack.org/developer/python-openstackclient/commands.html>`_
|
||||
@@ -178,5 +227,6 @@ Developer References
|
||||
for guidance on creating new OSC command interfaces.
|
||||
* See `OSC plugin <http://docs.openstack.org/developer/python-openstackclient/plugins.html>`_
|
||||
for information on the OSC plugin system to be used for ``neutron`` CLI extensions.
|
||||
* Create an OSC blueprint: https://blueprints.launchpad.net/python-openstackclient/
|
||||
* Report an OSC bug: https://bugs.launchpad.net/python-openstackclient/+filebug
|
||||
* Report an OpenStack Python SDK bug: https://bugs.launchpad.net/python-openstacksdk/+filebug
|
||||
|
||||
@@ -54,6 +54,18 @@ Now you can call various methods on the client instance.
|
||||
Alternatively, you can create a client instance using an auth token
|
||||
and a service endpoint URL directly.
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
>>> from neutronclient.v2_0 import client
|
||||
>>> neutron = client.Client(endpoint_url='http://192.168.206.130:9696/',
|
||||
token='d3f9226f27774f338019aa2611112ef6')
|
||||
... token='d3f9226f27774f338019aa2611112ef6')
|
||||
|
||||
You can get ``X-Openstack-Request-Id`` as ``request_ids`` from the result.
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
>>> network = {'name': 'mynetwork', 'admin_state_up': True}
|
||||
>>> neutron.create_network({'network':network})
|
||||
>>> networks = neutron.list_networks(name='mynetwork')
|
||||
>>> print networks.request_ids
|
||||
['req-978a0160-7ab0-44f0-8a93-08e9a4e785fa']
|
||||
|
||||
@@ -1,35 +0,0 @@
|
||||
# Copyright 2012 OpenStack Foundation.
|
||||
# All Rights Reserved
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
#
|
||||
|
||||
from 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)
|
||||
@@ -60,10 +60,20 @@ class NeutronClientException(NeutronException):
|
||||
"""
|
||||
|
||||
status_code = 0
|
||||
req_ids_msg = _("Neutron server returns request_ids: %s")
|
||||
request_ids = []
|
||||
|
||||
def __init__(self, message=None, **kwargs):
|
||||
self.request_ids = kwargs.get('request_ids')
|
||||
if 'status_code' in kwargs:
|
||||
self.status_code = kwargs['status_code']
|
||||
if self.request_ids:
|
||||
req_ids_msg = self.req_ids_msg % self.request_ids
|
||||
if message:
|
||||
message = _('%(msg)s\n%(id)s') % {'msg': message,
|
||||
'id': req_ids_msg}
|
||||
else:
|
||||
message = req_ids_msg
|
||||
super(NeutronClientException, self).__init__(message, **kwargs)
|
||||
|
||||
|
||||
|
||||
@@ -31,56 +31,56 @@ class NeutronClientExtension(neutronV20.NeutronCommand):
|
||||
|
||||
|
||||
class ClientExtensionShow(NeutronClientExtension, neutronV20.ShowCommand):
|
||||
def get_data(self, parsed_args):
|
||||
def take_action(self, parsed_args):
|
||||
# NOTE(mdietz): Calls 'execute' to provide a consistent pattern
|
||||
# for any implementers adding extensions with
|
||||
# regard to any other extension verb.
|
||||
return self.execute(parsed_args)
|
||||
|
||||
def execute(self, parsed_args):
|
||||
return super(ClientExtensionShow, self).get_data(parsed_args)
|
||||
return super(ClientExtensionShow, self).take_action(parsed_args)
|
||||
|
||||
|
||||
class ClientExtensionList(NeutronClientExtension, neutronV20.ListCommand):
|
||||
|
||||
def get_data(self, parsed_args):
|
||||
def take_action(self, parsed_args):
|
||||
# NOTE(mdietz): Calls 'execute' to provide a consistent pattern
|
||||
# for any implementers adding extensions with
|
||||
# regard to any other extension verb.
|
||||
return self.execute(parsed_args)
|
||||
|
||||
def execute(self, parsed_args):
|
||||
return super(ClientExtensionList, self).get_data(parsed_args)
|
||||
return super(ClientExtensionList, self).take_action(parsed_args)
|
||||
|
||||
|
||||
class ClientExtensionDelete(NeutronClientExtension, neutronV20.DeleteCommand):
|
||||
def run(self, parsed_args):
|
||||
def take_action(self, parsed_args):
|
||||
# NOTE(mdietz): Calls 'execute' to provide a consistent pattern
|
||||
# for any implementers adding extensions with
|
||||
# regard to any other extension verb.
|
||||
return self.execute(parsed_args)
|
||||
|
||||
def execute(self, parsed_args):
|
||||
return super(ClientExtensionDelete, self).run(parsed_args)
|
||||
return super(ClientExtensionDelete, self).take_action(parsed_args)
|
||||
|
||||
|
||||
class ClientExtensionCreate(NeutronClientExtension, neutronV20.CreateCommand):
|
||||
def get_data(self, parsed_args):
|
||||
def take_action(self, parsed_args):
|
||||
# NOTE(mdietz): Calls 'execute' to provide a consistent pattern
|
||||
# for any implementers adding extensions with
|
||||
# regard to any other extension verb.
|
||||
return self.execute(parsed_args)
|
||||
|
||||
def execute(self, parsed_args):
|
||||
return super(ClientExtensionCreate, self).get_data(parsed_args)
|
||||
return super(ClientExtensionCreate, self).take_action(parsed_args)
|
||||
|
||||
|
||||
class ClientExtensionUpdate(NeutronClientExtension, neutronV20.UpdateCommand):
|
||||
def run(self, parsed_args):
|
||||
def take_action(self, parsed_args):
|
||||
# NOTE(mdietz): Calls 'execute' to provide a consistent pattern
|
||||
# for any implementers adding extensions with
|
||||
# regard to any other extension verb.
|
||||
return self.execute(parsed_args)
|
||||
|
||||
def execute(self, parsed_args):
|
||||
return super(ClientExtensionUpdate, self).run(parsed_args)
|
||||
return super(ClientExtensionUpdate, self).take_action(parsed_args)
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
"""Utilities and helper functions."""
|
||||
|
||||
import argparse
|
||||
import functools
|
||||
import logging
|
||||
import netaddr
|
||||
import os
|
||||
@@ -46,6 +47,10 @@ def convert_to_uppercase(string):
|
||||
return string.upper()
|
||||
|
||||
|
||||
def convert_to_lowercase(string):
|
||||
return string.lower()
|
||||
|
||||
|
||||
def get_client_class(api_name, version, version_map):
|
||||
"""Returns the client class for the requested API version.
|
||||
|
||||
@@ -104,10 +109,21 @@ def str2bool(strbool):
|
||||
return strbool.lower() == 'true'
|
||||
|
||||
|
||||
def str2dict(strdict):
|
||||
def str2dict(strdict, required_keys=None, optional_keys=None):
|
||||
"""Convert key1=value1,key2=value2,... string into dictionary.
|
||||
|
||||
:param strdict: key1=value1,key2=value2
|
||||
:param strdict: string in the form of key1=value1,key2=value2
|
||||
:param required_keys: list of required keys. All keys in this list must be
|
||||
specified. Otherwise ArgumentTypeError will be raised.
|
||||
If this parameter is unspecified, no required key check
|
||||
will be done.
|
||||
:param optional_keys: list of optional keys.
|
||||
This parameter is used for valid key check.
|
||||
When at least one of required_keys and optional_keys,
|
||||
a key must be a member of either of required_keys or
|
||||
optional_keys. Otherwise, ArgumentTypeError will be
|
||||
raised. When both required_keys and optional_keys are
|
||||
unspecified, no valid key check will be done.
|
||||
"""
|
||||
result = {}
|
||||
if strdict:
|
||||
@@ -117,9 +133,29 @@ def str2dict(strdict):
|
||||
msg = _("invalid key-value '%s', expected format: key=value")
|
||||
raise argparse.ArgumentTypeError(msg % kv)
|
||||
result[key] = value
|
||||
valid_keys = set(required_keys or []) | set(optional_keys or [])
|
||||
if valid_keys:
|
||||
invalid_keys = [k for k in result if k not in valid_keys]
|
||||
if invalid_keys:
|
||||
msg = _("Invalid key(s) '%(invalid_keys)s' specified. "
|
||||
"Valid key(s): '%(valid_keys)s'.")
|
||||
raise argparse.ArgumentTypeError(
|
||||
msg % {'invalid_keys': ', '.join(sorted(invalid_keys)),
|
||||
'valid_keys': ', '.join(sorted(valid_keys))})
|
||||
if required_keys:
|
||||
not_found_keys = [k for k in required_keys if k not in result]
|
||||
if not_found_keys:
|
||||
msg = _("Required key(s) '%s' not specified.")
|
||||
raise argparse.ArgumentTypeError(msg % ', '.join(not_found_keys))
|
||||
return result
|
||||
|
||||
|
||||
def str2dict_type(optional_keys=None, required_keys=None):
|
||||
return functools.partial(str2dict,
|
||||
optional_keys=optional_keys,
|
||||
required_keys=required_keys)
|
||||
|
||||
|
||||
def http_log_req(_logger, args, kwargs):
|
||||
if not _logger.isEnabledFor(logging.DEBUG):
|
||||
return
|
||||
|
||||
@@ -18,17 +18,17 @@ from __future__ import print_function
|
||||
|
||||
import abc
|
||||
import argparse
|
||||
import functools
|
||||
import logging
|
||||
import re
|
||||
|
||||
from cliff.formatters import table
|
||||
from cliff import command
|
||||
from cliff import lister
|
||||
from cliff import show
|
||||
from oslo_serialization import jsonutils
|
||||
import six
|
||||
|
||||
from neutronclient._i18n import _
|
||||
from neutronclient.common import command
|
||||
from neutronclient.common import exceptions
|
||||
from neutronclient.common import utils
|
||||
|
||||
@@ -36,6 +36,7 @@ HEX_ELEM = '[0-9A-Fa-f]'
|
||||
UUID_PATTERN = '-'.join([HEX_ELEM + '{8}', HEX_ELEM + '{4}',
|
||||
HEX_ELEM + '{4}', HEX_ELEM + '{4}',
|
||||
HEX_ELEM + '{12}'])
|
||||
HYPHEN_OPTS = ['tags_any', 'not_tags', 'not_tags_any']
|
||||
|
||||
|
||||
def _get_resource_plural(resource, client):
|
||||
@@ -69,9 +70,8 @@ def find_resource_by_id(client, resource, resource_id, cmd_resource=None,
|
||||
not_found_message = (_("Unable to find %(resource)s with id "
|
||||
"'%(id)s'") %
|
||||
{'resource': resource, 'id': resource_id})
|
||||
# 404 is used to simulate server side behavior
|
||||
raise exceptions.NeutronClientException(
|
||||
message=not_found_message, status_code=404)
|
||||
# 404 is raised by exceptions.NotFound to simulate serverside behavior
|
||||
raise exceptions.NotFound(message=not_found_message)
|
||||
|
||||
|
||||
def find_resourceid_by_id(client, resource, resource_id, cmd_resource=None,
|
||||
@@ -106,9 +106,8 @@ def _find_resource_by_name(client, resource, name, project_id=None,
|
||||
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.NeutronClientException(
|
||||
message=not_found_message, status_code=404)
|
||||
# 404 is raised by exceptions.NotFound to simulate serverside behavior
|
||||
raise exceptions.NotFound(message=not_found_message)
|
||||
else:
|
||||
return info[0]
|
||||
|
||||
@@ -119,10 +118,18 @@ def find_resource_by_name_or_id(client, resource, name_or_id,
|
||||
try:
|
||||
return find_resource_by_id(client, resource, name_or_id,
|
||||
cmd_resource, parent_id, fields)
|
||||
except exceptions.NeutronClientException:
|
||||
return _find_resource_by_name(client, resource, name_or_id,
|
||||
project_id, cmd_resource, parent_id,
|
||||
fields)
|
||||
except exceptions.NotFound:
|
||||
try:
|
||||
return _find_resource_by_name(client, resource, name_or_id,
|
||||
project_id, cmd_resource, parent_id,
|
||||
fields)
|
||||
except exceptions.NotFound:
|
||||
not_found_message = (_("Unable to find %(resource)s with name "
|
||||
"or id '%(name_or_id)s'") %
|
||||
{'resource': resource,
|
||||
'name_or_id': name_or_id})
|
||||
raise exceptions.NotFound(
|
||||
message=not_found_message)
|
||||
|
||||
|
||||
def find_resourceid_by_name_or_id(client, resource, name_or_id,
|
||||
@@ -353,7 +360,7 @@ def _merge_args(qCmd, parsed_args, _extra_values, value_specs):
|
||||
if isinstance(arg_value, list):
|
||||
if value and isinstance(value, list):
|
||||
if (not arg_value or
|
||||
type(arg_value[0]) == type(value[0])):
|
||||
isinstance(arg_value[0], type(value[0]))):
|
||||
arg_value.extend(value)
|
||||
_extra_values.pop(key)
|
||||
|
||||
@@ -370,20 +377,7 @@ def update_dict(obj, dict, attributes):
|
||||
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-neutronclient/+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')
|
||||
|
||||
|
||||
# command.OpenStackCommand is abstract class so that metaclass of
|
||||
# cliff.command.Command is abstract class so that metaclass of
|
||||
# subclass must be subclass of metaclass of all its base.
|
||||
# otherwise metaclass conflict exception is raised.
|
||||
class NeutronCommandMeta(abc.ABCMeta):
|
||||
@@ -396,22 +390,17 @@ class NeutronCommandMeta(abc.ABCMeta):
|
||||
|
||||
|
||||
@six.add_metaclass(NeutronCommandMeta)
|
||||
class NeutronCommand(command.OpenStackCommand):
|
||||
class NeutronCommand(command.Command):
|
||||
|
||||
api = 'network'
|
||||
values_specs = []
|
||||
json_indent = None
|
||||
resource = None
|
||||
shadow_resource = None
|
||||
parent_id = None
|
||||
|
||||
def __init__(self, app, app_args):
|
||||
super(NeutronCommand, self).__init__(app, app_args)
|
||||
# NOTE(markmcclain): This is no longer supported in cliff version 1.5.2
|
||||
# see https://bugs.launchpad.net/python-neutronclient/+bug/1265926
|
||||
|
||||
# if hasattr(self, 'formatters'):
|
||||
# self.formatters['table'] = TableFormater()
|
||||
def run(self, parsed_args):
|
||||
self.log.debug('run(%s)', parsed_args)
|
||||
return super(NeutronCommand, self).run(parsed_args)
|
||||
|
||||
@property
|
||||
def cmd_resource(self):
|
||||
@@ -468,7 +457,6 @@ class NeutronCommand(command.OpenStackCommand):
|
||||
class CreateCommand(NeutronCommand, show.ShowOne):
|
||||
"""Create a resource for a given tenant."""
|
||||
|
||||
api = 'network'
|
||||
log = None
|
||||
|
||||
def get_parser(self, prog_name):
|
||||
@@ -482,8 +470,7 @@ class CreateCommand(NeutronCommand, show.ShowOne):
|
||||
self.add_known_arguments(parser)
|
||||
return parser
|
||||
|
||||
def get_data(self, parsed_args):
|
||||
self.log.debug('get_data(%s)' % parsed_args)
|
||||
def take_action(self, parsed_args):
|
||||
self.set_extra_attrs(parsed_args)
|
||||
neutron_client = self.get_client()
|
||||
_extra_values = parse_args_to_dict(self.values_specs)
|
||||
@@ -510,9 +497,9 @@ class CreateCommand(NeutronCommand, show.ShowOne):
|
||||
class UpdateCommand(NeutronCommand):
|
||||
"""Update resource's information."""
|
||||
|
||||
api = 'network'
|
||||
log = None
|
||||
allow_names = True
|
||||
help_resource = None
|
||||
|
||||
def get_parser(self, prog_name):
|
||||
parser = super(UpdateCommand, self).get_parser(prog_name)
|
||||
@@ -520,14 +507,15 @@ class UpdateCommand(NeutronCommand):
|
||||
help_str = _('ID or name of %s to update.')
|
||||
else:
|
||||
help_str = _('ID of %s to update.')
|
||||
if not self.help_resource:
|
||||
self.help_resource = self.resource
|
||||
parser.add_argument(
|
||||
'id', metavar=self.resource.upper(),
|
||||
help=help_str % self.resource)
|
||||
help=help_str % self.help_resource)
|
||||
self.add_known_arguments(parser)
|
||||
return parser
|
||||
|
||||
def run(self, parsed_args):
|
||||
self.log.debug('run(%s)', parsed_args)
|
||||
def take_action(self, parsed_args):
|
||||
self.set_extra_attrs(parsed_args)
|
||||
neutron_client = self.get_client()
|
||||
_extra_values = parse_args_to_dict(self.values_specs)
|
||||
@@ -565,9 +553,9 @@ class UpdateCommand(NeutronCommand):
|
||||
class DeleteCommand(NeutronCommand):
|
||||
"""Delete a given resource."""
|
||||
|
||||
api = 'network'
|
||||
log = None
|
||||
allow_names = True
|
||||
help_resource = None
|
||||
|
||||
def get_parser(self, prog_name):
|
||||
parser = super(DeleteCommand, self).get_parser(prog_name)
|
||||
@@ -575,14 +563,15 @@ class DeleteCommand(NeutronCommand):
|
||||
help_str = _('ID or name of %s to delete.')
|
||||
else:
|
||||
help_str = _('ID of %s to delete.')
|
||||
if not self.help_resource:
|
||||
self.help_resource = self.resource
|
||||
parser.add_argument(
|
||||
'id', metavar=self.resource.upper(),
|
||||
help=help_str % self.resource)
|
||||
help=help_str % self.help_resource)
|
||||
self.add_known_arguments(parser)
|
||||
return parser
|
||||
|
||||
def run(self, parsed_args):
|
||||
self.log.debug('run(%s)', parsed_args)
|
||||
def take_action(self, parsed_args):
|
||||
self.set_extra_attrs(parsed_args)
|
||||
neutron_client = self.get_client()
|
||||
obj_deleter = getattr(neutron_client,
|
||||
@@ -611,13 +600,43 @@ class DeleteCommand(NeutronCommand):
|
||||
class ListCommand(NeutronCommand, lister.Lister):
|
||||
"""List resources that belong to a given tenant."""
|
||||
|
||||
api = 'network'
|
||||
log = None
|
||||
_formatters = {}
|
||||
list_columns = []
|
||||
unknown_parts_flag = True
|
||||
pagination_support = False
|
||||
sorting_support = False
|
||||
resource_plural = None
|
||||
|
||||
# A list to define arguments for filtering by attribute value
|
||||
# CLI arguments are shown in the order of this list.
|
||||
# Each element must be either of a string of an attribute name
|
||||
# or a dict of a full attribute definitions whose format is:
|
||||
# {'name': attribute name, (mandatory)
|
||||
# 'help': help message for CLI (mandatory)
|
||||
# 'boolean': boolean parameter or not. (Default: False) (optional)
|
||||
# 'argparse_kwargs': a dict of parameters passed to
|
||||
# argparse add_argument()
|
||||
# (Default: {}) (optional)
|
||||
# }
|
||||
# For more details, see ListNetworks.filter_attrs.
|
||||
filter_attrs = []
|
||||
|
||||
default_attr_defs = {
|
||||
'name': {
|
||||
'help': _("Filter %s according to their name."),
|
||||
'boolean': False,
|
||||
},
|
||||
'tenant_id': {
|
||||
'help': _('Filter %s belonging to the given tenant.'),
|
||||
'boolean': False,
|
||||
},
|
||||
'admin_state_up': {
|
||||
'help': _('Filter and list the %s whose administrative '
|
||||
'state is active'),
|
||||
'boolean': True,
|
||||
},
|
||||
}
|
||||
|
||||
def get_parser(self, prog_name):
|
||||
parser = super(ListCommand, self).get_parser(prog_name)
|
||||
@@ -627,8 +646,36 @@ class ListCommand(NeutronCommand, lister.Lister):
|
||||
if self.sorting_support:
|
||||
add_sorting_argument(parser)
|
||||
self.add_known_arguments(parser)
|
||||
self.add_filtering_arguments(parser)
|
||||
return parser
|
||||
|
||||
def add_filtering_arguments(self, parser):
|
||||
if not self.filter_attrs:
|
||||
return
|
||||
|
||||
group_parser = parser.add_argument_group('filtering arguments')
|
||||
collection = self.resource_plural or '%ss' % self.resource
|
||||
for attr in self.filter_attrs:
|
||||
if isinstance(attr, str):
|
||||
# Use detail defined in default_attr_defs
|
||||
attr_name = attr
|
||||
attr_defs = self.default_attr_defs[attr]
|
||||
else:
|
||||
attr_name = attr['name']
|
||||
attr_defs = attr
|
||||
option_name = '--%s' % attr_name.replace('_', '-')
|
||||
params = attr_defs.get('argparse_kwargs', {})
|
||||
try:
|
||||
help_msg = attr_defs['help'] % collection
|
||||
except TypeError:
|
||||
help_msg = attr_defs['help']
|
||||
if attr_defs.get('boolean', False):
|
||||
add_arg_func = functools.partial(utils.add_boolean_argument,
|
||||
group_parser)
|
||||
else:
|
||||
add_arg_func = group_parser.add_argument
|
||||
add_arg_func(option_name, help=help_msg, **params)
|
||||
|
||||
def args2search_opts(self, parsed_args):
|
||||
search_opts = {}
|
||||
fields = parsed_args.fields
|
||||
@@ -636,6 +683,14 @@ class ListCommand(NeutronCommand, lister.Lister):
|
||||
search_opts.update({'fields': fields})
|
||||
if parsed_args.show_details:
|
||||
search_opts.update({'verbose': 'True'})
|
||||
filter_attrs = [field if isinstance(field, str) else field['name']
|
||||
for field in self.filter_attrs]
|
||||
for attr in filter_attrs:
|
||||
val = getattr(parsed_args, attr, None)
|
||||
if attr in HYPHEN_OPTS:
|
||||
attr = attr.replace('_', '-')
|
||||
if val:
|
||||
search_opts[attr] = val
|
||||
return search_opts
|
||||
|
||||
def call_server(self, neutron_client, search_opts, parsed_args):
|
||||
@@ -706,8 +761,7 @@ class ListCommand(NeutronCommand, lister.Lister):
|
||||
s, _columns, formatters=formatters, )
|
||||
for s in info), )
|
||||
|
||||
def get_data(self, parsed_args):
|
||||
self.log.debug('get_data(%s)', parsed_args)
|
||||
def take_action(self, parsed_args):
|
||||
self.set_extra_attrs(parsed_args)
|
||||
data = self.retrieve_list(parsed_args)
|
||||
self.extend_list(data, parsed_args)
|
||||
@@ -717,9 +771,9 @@ class ListCommand(NeutronCommand, lister.Lister):
|
||||
class ShowCommand(NeutronCommand, show.ShowOne):
|
||||
"""Show information of a given resource."""
|
||||
|
||||
api = 'network'
|
||||
log = None
|
||||
allow_names = True
|
||||
help_resource = None
|
||||
|
||||
def get_parser(self, prog_name):
|
||||
parser = super(ShowCommand, self).get_parser(prog_name)
|
||||
@@ -728,14 +782,15 @@ class ShowCommand(NeutronCommand, show.ShowOne):
|
||||
help_str = _('ID or name of %s to look up.')
|
||||
else:
|
||||
help_str = _('ID of %s to look up.')
|
||||
if not self.help_resource:
|
||||
self.help_resource = self.resource
|
||||
parser.add_argument(
|
||||
'id', metavar=self.resource.upper(),
|
||||
help=help_str % self.resource)
|
||||
help=help_str % self.help_resource)
|
||||
self.add_known_arguments(parser)
|
||||
return parser
|
||||
|
||||
def get_data(self, parsed_args):
|
||||
self.log.debug('get_data(%s)', parsed_args)
|
||||
def take_action(self, parsed_args):
|
||||
self.set_extra_attrs(parsed_args)
|
||||
neutron_client = self.get_client()
|
||||
|
||||
|
||||
@@ -22,7 +22,7 @@ class ListAddressScope(neutronV20.ListCommand):
|
||||
"""List address scopes that belong to a given tenant."""
|
||||
|
||||
resource = 'address_scope'
|
||||
list_columns = ['id', 'name']
|
||||
list_columns = ['id', 'name', 'ip_version']
|
||||
pagination_support = True
|
||||
sorting_support = True
|
||||
|
||||
@@ -45,10 +45,18 @@ class CreateAddressScope(neutronV20.CreateCommand):
|
||||
help=_('Set the address scope as shared.'))
|
||||
parser.add_argument(
|
||||
'name',
|
||||
metavar='NAME',
|
||||
help=_('Specify the name of the address scope.'))
|
||||
parser.add_argument(
|
||||
'ip_version',
|
||||
metavar='IP_VERSION',
|
||||
type=int,
|
||||
choices=[4, 6],
|
||||
help=_('Specify the address family of the address scope.'))
|
||||
|
||||
def args2body(self, parsed_args):
|
||||
body = {'name': parsed_args.name}
|
||||
body = {'name': parsed_args.name,
|
||||
'ip_version': parsed_args.ip_version}
|
||||
if parsed_args.shared:
|
||||
body['shared'] = True
|
||||
neutronV20.update_dict(parsed_args, body, ['tenant_id'])
|
||||
|
||||
@@ -32,14 +32,15 @@ class AddNetworkToDhcpAgent(neutronV20.NeutronCommand):
|
||||
parser = super(AddNetworkToDhcpAgent, self).get_parser(prog_name)
|
||||
parser.add_argument(
|
||||
'dhcp_agent',
|
||||
metavar='DHCP_AGENT',
|
||||
help=_('ID of the DHCP agent.'))
|
||||
parser.add_argument(
|
||||
'network',
|
||||
metavar='NETWORK',
|
||||
help=_('Network to add.'))
|
||||
return parser
|
||||
|
||||
def run(self, parsed_args):
|
||||
self.log.debug('run(%s)' % parsed_args)
|
||||
def take_action(self, parsed_args):
|
||||
neutron_client = self.get_client()
|
||||
_net_id = neutronV20.find_resourceid_by_name_or_id(
|
||||
neutron_client, 'network', parsed_args.network)
|
||||
@@ -56,14 +57,15 @@ class RemoveNetworkFromDhcpAgent(neutronV20.NeutronCommand):
|
||||
parser = super(RemoveNetworkFromDhcpAgent, self).get_parser(prog_name)
|
||||
parser.add_argument(
|
||||
'dhcp_agent',
|
||||
metavar='DHCP_AGENT',
|
||||
help=_('ID of the DHCP agent.'))
|
||||
parser.add_argument(
|
||||
'network',
|
||||
metavar='NETWORK',
|
||||
help=_('Network to remove.'))
|
||||
return parser
|
||||
|
||||
def run(self, parsed_args):
|
||||
self.log.debug('run(%s)' % parsed_args)
|
||||
def take_action(self, parsed_args):
|
||||
neutron_client = self.get_client()
|
||||
_net_id = neutronV20.find_resourceid_by_name_or_id(
|
||||
neutron_client, 'network', parsed_args.network)
|
||||
@@ -83,6 +85,7 @@ class ListNetworksOnDhcpAgent(network.ListNetwork):
|
||||
self).get_parser(prog_name)
|
||||
parser.add_argument(
|
||||
'dhcp_agent',
|
||||
metavar='DHCP_AGENT',
|
||||
help=_('ID of the DHCP agent.'))
|
||||
return parser
|
||||
|
||||
@@ -105,6 +108,7 @@ class ListDhcpAgentsHostingNetwork(neutronV20.ListCommand):
|
||||
self).get_parser(prog_name)
|
||||
parser.add_argument(
|
||||
'network',
|
||||
metavar='NETWORK',
|
||||
help=_('Network to query.'))
|
||||
return parser
|
||||
|
||||
@@ -128,14 +132,15 @@ class AddRouterToL3Agent(neutronV20.NeutronCommand):
|
||||
parser = super(AddRouterToL3Agent, self).get_parser(prog_name)
|
||||
parser.add_argument(
|
||||
'l3_agent',
|
||||
metavar='L3_AGENT',
|
||||
help=_('ID of the L3 agent.'))
|
||||
parser.add_argument(
|
||||
'router',
|
||||
metavar='ROUTER',
|
||||
help=_('Router to add.'))
|
||||
return parser
|
||||
|
||||
def run(self, parsed_args):
|
||||
self.log.debug('run(%s)' % parsed_args)
|
||||
def take_action(self, parsed_args):
|
||||
neutron_client = self.get_client()
|
||||
_id = neutronV20.find_resourceid_by_name_or_id(
|
||||
neutron_client, 'router', parsed_args.router)
|
||||
@@ -152,14 +157,15 @@ class RemoveRouterFromL3Agent(neutronV20.NeutronCommand):
|
||||
parser = super(RemoveRouterFromL3Agent, self).get_parser(prog_name)
|
||||
parser.add_argument(
|
||||
'l3_agent',
|
||||
metavar='L3_AGENT',
|
||||
help=_('ID of the L3 agent.'))
|
||||
parser.add_argument(
|
||||
'router',
|
||||
metavar='ROUTER',
|
||||
help=_('Router to remove.'))
|
||||
return parser
|
||||
|
||||
def run(self, parsed_args):
|
||||
self.log.debug('run(%s)' % parsed_args)
|
||||
def take_action(self, parsed_args):
|
||||
neutron_client = self.get_client()
|
||||
_id = neutronV20.find_resourceid_by_name_or_id(
|
||||
neutron_client, 'router', parsed_args.router)
|
||||
@@ -183,6 +189,7 @@ class ListRoutersOnL3Agent(neutronV20.ListCommand):
|
||||
self).get_parser(prog_name)
|
||||
parser.add_argument(
|
||||
'l3_agent',
|
||||
metavar='L3_AGENT',
|
||||
help=_('ID of the L3 agent to query.'))
|
||||
return parser
|
||||
|
||||
@@ -204,6 +211,7 @@ class ListL3AgentsHostingRouter(neutronV20.ListCommand):
|
||||
parser = super(ListL3AgentsHostingRouter,
|
||||
self).get_parser(prog_name)
|
||||
parser.add_argument('router',
|
||||
metavar='ROUTER',
|
||||
help=_('Router to query.'))
|
||||
return parser
|
||||
|
||||
@@ -236,6 +244,7 @@ class ListPoolsOnLbaasAgent(neutronV20.ListCommand):
|
||||
parser = super(ListPoolsOnLbaasAgent, self).get_parser(prog_name)
|
||||
parser.add_argument(
|
||||
'lbaas_agent',
|
||||
metavar='LBAAS_AGENT',
|
||||
help=_('ID of the loadbalancer agent to query.'))
|
||||
return parser
|
||||
|
||||
@@ -260,6 +269,7 @@ class GetLbaasAgentHostingPool(neutronV20.ListCommand):
|
||||
parser = super(GetLbaasAgentHostingPool,
|
||||
self).get_parser(prog_name)
|
||||
parser.add_argument('pool',
|
||||
metavar='POOL',
|
||||
help=_('Pool to query.'))
|
||||
return parser
|
||||
|
||||
@@ -289,6 +299,7 @@ class ListLoadBalancersOnLbaasAgent(neutronV20.ListCommand):
|
||||
prog_name)
|
||||
parser.add_argument(
|
||||
'lbaas_agent',
|
||||
metavar='LBAAS_AGENT',
|
||||
help=_('ID of the loadbalancer agent to query.'))
|
||||
return parser
|
||||
|
||||
@@ -313,6 +324,7 @@ class GetLbaasAgentHostingLoadBalancer(neutronV20.ListCommand):
|
||||
parser = super(GetLbaasAgentHostingLoadBalancer,
|
||||
self).get_parser(prog_name)
|
||||
parser.add_argument('loadbalancer',
|
||||
metavar='LOADBALANCER',
|
||||
help=_('LoadBalancer to query.'))
|
||||
return parser
|
||||
|
||||
|
||||
81
neutronclient/neutron/v2_0/auto_allocated_topology.py
Executable file
81
neutronclient/neutron/v2_0/auto_allocated_topology.py
Executable file
@@ -0,0 +1,81 @@
|
||||
# Copyright 2016 IBM
|
||||
# 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 argparse
|
||||
from cliff import show
|
||||
from oslo_serialization import jsonutils
|
||||
|
||||
from neutronclient._i18n import _
|
||||
from neutronclient.common import exceptions
|
||||
from neutronclient.neutron import v2_0
|
||||
|
||||
|
||||
class ShowAutoAllocatedTopology(v2_0.NeutronCommand, show.ShowOne):
|
||||
"""Show the auto-allocated topology of a given tenant."""
|
||||
|
||||
resource = 'auto_allocated_topology'
|
||||
|
||||
def get_parser(self, prog_name):
|
||||
parser = super(ShowAutoAllocatedTopology, self).get_parser(prog_name)
|
||||
parser.add_argument(
|
||||
'--dry-run',
|
||||
help=_('Validate the requirements for auto-allocated-topology. '
|
||||
'(Does not return a topology.)'),
|
||||
action='store_true')
|
||||
parser.add_argument(
|
||||
'--tenant-id', metavar='tenant-id',
|
||||
help=_('The owner tenant ID.'))
|
||||
# Allow people to do
|
||||
# neutron auto-allocated-topology-show <tenant-id>
|
||||
# (Only useful to users who can look at other tenants' topologies.)
|
||||
# We use a different name for this arg because the default will
|
||||
# override whatever is in the named arg otherwise.
|
||||
parser.add_argument(
|
||||
'pos_tenant_id',
|
||||
help=argparse.SUPPRESS, nargs='?')
|
||||
return parser
|
||||
|
||||
def take_action(self, parsed_args):
|
||||
client = self.get_client()
|
||||
extra_values = v2_0.parse_args_to_dict(self.values_specs)
|
||||
if extra_values:
|
||||
raise exceptions.CommandError(
|
||||
_("Invalid argument(s): --%s") % ', --'.join(extra_values))
|
||||
tenant_id = parsed_args.tenant_id or parsed_args.pos_tenant_id
|
||||
if parsed_args.dry_run:
|
||||
data = client.validate_auto_allocated_topology_requirements(
|
||||
tenant_id)
|
||||
else:
|
||||
data = client.get_auto_allocated_topology(tenant_id)
|
||||
if self.resource in data:
|
||||
for k, v in data[self.resource].items():
|
||||
if isinstance(v, list):
|
||||
value = ""
|
||||
for _item in v:
|
||||
if value:
|
||||
value += "\n"
|
||||
if isinstance(_item, dict):
|
||||
value += jsonutils.dumps(_item)
|
||||
else:
|
||||
value += str(_item)
|
||||
data[self.resource][k] = value
|
||||
elif v == "dry-run=pass":
|
||||
return ("dry-run",), ("pass",)
|
||||
elif v is None:
|
||||
data[self.resource][k] = ''
|
||||
return zip(*sorted(data[self.resource].items()))
|
||||
else:
|
||||
return None
|
||||
@@ -10,7 +10,7 @@
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
from neutronclient.i18n import _
|
||||
from neutronclient._i18n import _
|
||||
from neutronclient.neutron import v2_0 as neutronv20
|
||||
|
||||
|
||||
|
||||
0
neutronclient/neutron/v2_0/bgp/__init__.py
Normal file
0
neutronclient/neutron/v2_0/bgp/__init__.py
Normal file
117
neutronclient/neutron/v2_0/bgp/dragentscheduler.py
Normal file
117
neutronclient/neutron/v2_0/bgp/dragentscheduler.py
Normal file
@@ -0,0 +1,117 @@
|
||||
# Copyright 2016 Huawei Technologies India Pvt. Ltd.
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
#
|
||||
|
||||
from __future__ import print_function
|
||||
|
||||
from neutronclient._i18n import _
|
||||
from neutronclient.neutron import v2_0 as neutronV20
|
||||
from neutronclient.neutron.v2_0.bgp import speaker as bgp_speaker
|
||||
|
||||
|
||||
def add_common_args(parser):
|
||||
parser.add_argument('dragent_id',
|
||||
metavar='BGP_DRAGENT_ID',
|
||||
help=_('ID of the Dynamic Routing agent.'))
|
||||
parser.add_argument('bgp_speaker',
|
||||
metavar='BGP_SPEAKER',
|
||||
help=_('ID or name of the BGP speaker.'))
|
||||
|
||||
|
||||
class AddBGPSpeakerToDRAgent(neutronV20.NeutronCommand):
|
||||
"""Add a BGP speaker to a Dynamic Routing agent."""
|
||||
|
||||
def get_parser(self, prog_name):
|
||||
parser = super(AddBGPSpeakerToDRAgent, self).get_parser(prog_name)
|
||||
add_common_args(parser)
|
||||
return parser
|
||||
|
||||
def take_action(self, parsed_args):
|
||||
neutron_client = self.get_client()
|
||||
_speaker_id = bgp_speaker.get_bgp_speaker_id(neutron_client,
|
||||
parsed_args.bgp_speaker)
|
||||
neutron_client.add_bgp_speaker_to_dragent(
|
||||
parsed_args.dragent_id, {'bgp_speaker_id': _speaker_id})
|
||||
print(_('Associated BGP speaker %s to the Dynamic Routing agent.')
|
||||
% parsed_args.bgp_speaker, file=self.app.stdout)
|
||||
|
||||
|
||||
class RemoveBGPSpeakerFromDRAgent(neutronV20.NeutronCommand):
|
||||
"""Removes a BGP speaker from a Dynamic Routing agent."""
|
||||
|
||||
def get_parser(self, prog_name):
|
||||
parser = super(RemoveBGPSpeakerFromDRAgent, self).get_parser(
|
||||
prog_name)
|
||||
add_common_args(parser)
|
||||
return parser
|
||||
|
||||
def take_action(self, parsed_args):
|
||||
neutron_client = self.get_client()
|
||||
_speaker_id = bgp_speaker.get_bgp_speaker_id(neutron_client,
|
||||
parsed_args.bgp_speaker)
|
||||
neutron_client.remove_bgp_speaker_from_dragent(parsed_args.dragent_id,
|
||||
_speaker_id)
|
||||
print(_('Disassociated BGP speaker %s from the '
|
||||
'Dynamic Routing agent.')
|
||||
% parsed_args.bgp_speaker, file=self.app.stdout)
|
||||
|
||||
|
||||
class ListBGPSpeakersOnDRAgent(neutronV20.ListCommand):
|
||||
"""List BGP speakers hosted by a Dynamic Routing agent."""
|
||||
|
||||
list_columns = ['id', 'name', 'local_as', 'ip_version']
|
||||
resource = 'bgp_speaker'
|
||||
|
||||
def get_parser(self, prog_name):
|
||||
parser = super(ListBGPSpeakersOnDRAgent,
|
||||
self).get_parser(prog_name)
|
||||
parser.add_argument(
|
||||
'dragent_id',
|
||||
metavar='BGP_DRAGENT_ID',
|
||||
help=_('ID of the Dynamic Routing agent.'))
|
||||
return parser
|
||||
|
||||
def call_server(self, neutron_client, search_opts, parsed_args):
|
||||
data = neutron_client.list_bgp_speaker_on_dragent(
|
||||
parsed_args.dragent_id, **search_opts)
|
||||
return data
|
||||
|
||||
|
||||
class ListDRAgentsHostingBGPSpeaker(neutronV20.ListCommand):
|
||||
"""List Dynamic Routing agents hosting a BGP speaker."""
|
||||
|
||||
resource = 'agent'
|
||||
_formatters = {}
|
||||
list_columns = ['id', 'host', 'admin_state_up', 'alive']
|
||||
unknown_parts_flag = False
|
||||
|
||||
def get_parser(self, prog_name):
|
||||
parser = super(ListDRAgentsHostingBGPSpeaker,
|
||||
self).get_parser(prog_name)
|
||||
parser.add_argument('bgp_speaker',
|
||||
metavar='BGP_SPEAKER',
|
||||
help=_('ID or name of the BGP speaker.'))
|
||||
return parser
|
||||
|
||||
def extend_list(self, data, parsed_args):
|
||||
for agent in data:
|
||||
agent['alive'] = ":-)" if agent['alive'] else 'xxx'
|
||||
|
||||
def call_server(self, neutron_client, search_opts, parsed_args):
|
||||
_speaker_id = bgp_speaker.get_bgp_speaker_id(neutron_client,
|
||||
parsed_args.bgp_speaker)
|
||||
search_opts['bgp_speaker'] = _speaker_id
|
||||
data = neutron_client.list_dragents_hosting_bgp_speaker(**search_opts)
|
||||
return data
|
||||
127
neutronclient/neutron/v2_0/bgp/peer.py
Normal file
127
neutronclient/neutron/v2_0/bgp/peer.py
Normal file
@@ -0,0 +1,127 @@
|
||||
# Copyright 2016 Huawei Technologies India Pvt. Ltd.
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
#
|
||||
|
||||
from neutronclient._i18n import _
|
||||
from neutronclient.common import exceptions
|
||||
from neutronclient.common import utils
|
||||
from neutronclient.common import validators
|
||||
from neutronclient.neutron import v2_0 as neutronv20
|
||||
|
||||
|
||||
def get_bgp_peer_id(client, id_or_name):
|
||||
return neutronv20.find_resourceid_by_name_or_id(client,
|
||||
'bgp_peer',
|
||||
id_or_name)
|
||||
|
||||
|
||||
def validate_peer_attributes(parsed_args):
|
||||
# Validate AS number
|
||||
validators.validate_int_range(parsed_args, 'remote_as',
|
||||
neutronv20.bgp.speaker.MIN_AS_NUM,
|
||||
neutronv20.bgp.speaker.MAX_AS_NUM)
|
||||
# Validate password
|
||||
if parsed_args.auth_type != 'none' and parsed_args.password is None:
|
||||
raise exceptions.CommandError(_('Must provide password if auth-type '
|
||||
'is specified.'))
|
||||
if parsed_args.auth_type == 'none' and parsed_args.password:
|
||||
raise exceptions.CommandError(_('Must provide auth-type if password '
|
||||
'is specified.'))
|
||||
|
||||
|
||||
class ListPeers(neutronv20.ListCommand):
|
||||
"""List BGP peers."""
|
||||
|
||||
resource = 'bgp_peer'
|
||||
list_columns = ['id', 'name', 'peer_ip', 'remote_as']
|
||||
pagination_support = True
|
||||
sorting_support = True
|
||||
|
||||
|
||||
class ShowPeer(neutronv20.ShowCommand):
|
||||
"""Show information of a given BGP peer."""
|
||||
|
||||
resource = 'bgp_peer'
|
||||
|
||||
|
||||
class CreatePeer(neutronv20.CreateCommand):
|
||||
"""Create a BGP Peer."""
|
||||
|
||||
resource = 'bgp_peer'
|
||||
|
||||
def add_known_arguments(self, parser):
|
||||
parser.add_argument(
|
||||
'name',
|
||||
metavar='NAME',
|
||||
help=_('Name of the BGP peer to create.'))
|
||||
parser.add_argument(
|
||||
'--peer-ip',
|
||||
metavar='PEER_IP_ADDRESS',
|
||||
required=True,
|
||||
help=_('Peer IP address.'))
|
||||
parser.add_argument(
|
||||
'--remote-as',
|
||||
required=True,
|
||||
metavar='PEER_REMOTE_AS',
|
||||
help=_('Peer AS number. (Integer in [%(min_val)s, %(max_val)s] '
|
||||
'is allowed.)') %
|
||||
{'min_val': neutronv20.bgp.speaker.MIN_AS_NUM,
|
||||
'max_val': neutronv20.bgp.speaker.MAX_AS_NUM})
|
||||
parser.add_argument(
|
||||
'--auth-type',
|
||||
metavar='PEER_AUTH_TYPE',
|
||||
choices=['none', 'md5'],
|
||||
default='none',
|
||||
type=utils.convert_to_lowercase,
|
||||
help=_('Authentication algorithm. Supported algorithms: '
|
||||
'none(default), md5'))
|
||||
parser.add_argument(
|
||||
'--password',
|
||||
metavar='AUTH_PASSWORD',
|
||||
help=_('Authentication password.'))
|
||||
|
||||
def args2body(self, parsed_args):
|
||||
body = {}
|
||||
validate_peer_attributes(parsed_args)
|
||||
neutronv20.update_dict(parsed_args, body,
|
||||
['name', 'peer_ip',
|
||||
'remote_as', 'auth_type', 'password'])
|
||||
return {self.resource: body}
|
||||
|
||||
|
||||
class UpdatePeer(neutronv20.UpdateCommand):
|
||||
"""Update BGP Peer's information."""
|
||||
|
||||
resource = 'bgp_peer'
|
||||
|
||||
def add_known_arguments(self, parser):
|
||||
parser.add_argument(
|
||||
'--name',
|
||||
help=_('Updated name of the BGP peer.'))
|
||||
parser.add_argument(
|
||||
'--password',
|
||||
metavar='AUTH_PASSWORD',
|
||||
help=_('Updated authentication password.'))
|
||||
|
||||
def args2body(self, parsed_args):
|
||||
body = {}
|
||||
neutronv20.update_dict(parsed_args, body, ['name', 'password'])
|
||||
return {self.resource: body}
|
||||
|
||||
|
||||
class DeletePeer(neutronv20.DeleteCommand):
|
||||
"""Delete a BGP peer."""
|
||||
|
||||
resource = 'bgp_peer'
|
||||
277
neutronclient/neutron/v2_0/bgp/speaker.py
Executable file
277
neutronclient/neutron/v2_0/bgp/speaker.py
Executable file
@@ -0,0 +1,277 @@
|
||||
# Copyright 2016 Huawei Technologies India Pvt. Ltd.
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
#
|
||||
|
||||
from __future__ import print_function
|
||||
|
||||
from neutronclient._i18n import _
|
||||
from neutronclient.common import utils
|
||||
from neutronclient.common import validators
|
||||
from neutronclient.neutron import v2_0 as neutronv20
|
||||
from neutronclient.neutron.v2_0.bgp import peer as bgp_peer
|
||||
|
||||
# Allowed BGP Autonomous number range
|
||||
MIN_AS_NUM = 1
|
||||
MAX_AS_NUM = 65535
|
||||
|
||||
|
||||
def get_network_id(client, id_or_name):
|
||||
return neutronv20.find_resourceid_by_name_or_id(client,
|
||||
'network',
|
||||
id_or_name)
|
||||
|
||||
|
||||
def get_bgp_speaker_id(client, id_or_name):
|
||||
return neutronv20.find_resourceid_by_name_or_id(client,
|
||||
'bgp_speaker',
|
||||
id_or_name)
|
||||
|
||||
|
||||
def validate_speaker_attributes(parsed_args):
|
||||
# Validate AS number
|
||||
validators.validate_int_range(parsed_args, 'local_as',
|
||||
MIN_AS_NUM, MAX_AS_NUM)
|
||||
|
||||
|
||||
def add_common_arguments(parser):
|
||||
utils.add_boolean_argument(
|
||||
parser, '--advertise-floating-ip-host-routes',
|
||||
help=_('Whether to enable or disable the advertisement '
|
||||
'of floating-ip host routes by the BGP speaker. '
|
||||
'By default floating ip host routes will be '
|
||||
'advertised by the BGP speaker.'))
|
||||
utils.add_boolean_argument(
|
||||
parser, '--advertise-tenant-networks',
|
||||
help=_('Whether to enable or disable the advertisement '
|
||||
'of tenant network routes by the BGP speaker. '
|
||||
'By default tenant network routes will be '
|
||||
'advertised by the BGP speaker.'))
|
||||
|
||||
|
||||
def args2body_common_arguments(body, parsed_args):
|
||||
neutronv20.update_dict(parsed_args, body,
|
||||
['name',
|
||||
'advertise_floating_ip_host_routes',
|
||||
'advertise_tenant_networks'])
|
||||
|
||||
|
||||
class ListSpeakers(neutronv20.ListCommand):
|
||||
"""List BGP speakers."""
|
||||
|
||||
resource = 'bgp_speaker'
|
||||
list_columns = ['id', 'name', 'local_as', 'ip_version']
|
||||
pagination_support = True
|
||||
sorting_support = True
|
||||
|
||||
|
||||
class ShowSpeaker(neutronv20.ShowCommand):
|
||||
"""Show information of a given BGP speaker."""
|
||||
|
||||
resource = 'bgp_speaker'
|
||||
|
||||
|
||||
class CreateSpeaker(neutronv20.CreateCommand):
|
||||
"""Create a BGP Speaker."""
|
||||
|
||||
resource = 'bgp_speaker'
|
||||
|
||||
def add_known_arguments(self, parser):
|
||||
parser.add_argument(
|
||||
'name',
|
||||
metavar='NAME',
|
||||
help=_('Name of the BGP speaker to create.'))
|
||||
parser.add_argument(
|
||||
'--local-as',
|
||||
metavar='LOCAL_AS',
|
||||
required=True,
|
||||
help=_('Local AS number. (Integer in [%(min_val)s, %(max_val)s] '
|
||||
'is allowed.)') % {'min_val': MIN_AS_NUM,
|
||||
'max_val': MAX_AS_NUM})
|
||||
parser.add_argument(
|
||||
'--ip-version',
|
||||
type=int, choices=[4, 6],
|
||||
default=4,
|
||||
help=_('IP version for the BGP speaker (default is 4).'))
|
||||
add_common_arguments(parser)
|
||||
|
||||
def args2body(self, parsed_args):
|
||||
body = {}
|
||||
validate_speaker_attributes(parsed_args)
|
||||
body['local_as'] = parsed_args.local_as
|
||||
body['ip_version'] = parsed_args.ip_version
|
||||
args2body_common_arguments(body, parsed_args)
|
||||
return {self.resource: body}
|
||||
|
||||
|
||||
class UpdateSpeaker(neutronv20.UpdateCommand):
|
||||
"""Update BGP Speaker's information."""
|
||||
|
||||
resource = 'bgp_speaker'
|
||||
|
||||
def add_known_arguments(self, parser):
|
||||
parser.add_argument(
|
||||
'--name',
|
||||
help=_('Name of the BGP speaker to update.'))
|
||||
add_common_arguments(parser)
|
||||
|
||||
def args2body(self, parsed_args):
|
||||
body = {}
|
||||
args2body_common_arguments(body, parsed_args)
|
||||
return {self.resource: body}
|
||||
|
||||
|
||||
class DeleteSpeaker(neutronv20.DeleteCommand):
|
||||
"""Delete a BGP speaker."""
|
||||
|
||||
resource = 'bgp_speaker'
|
||||
|
||||
|
||||
class AddPeerToSpeaker(neutronv20.NeutronCommand):
|
||||
"""Add a peer to the BGP speaker."""
|
||||
|
||||
def get_parser(self, prog_name):
|
||||
parser = super(AddPeerToSpeaker, self).get_parser(prog_name)
|
||||
parser.add_argument(
|
||||
'bgp_speaker',
|
||||
metavar='BGP_SPEAKER',
|
||||
help=_('ID or name of the BGP speaker.'))
|
||||
parser.add_argument(
|
||||
'bgp_peer',
|
||||
metavar='BGP_PEER',
|
||||
help=_('ID or name of the BGP peer to add.'))
|
||||
return parser
|
||||
|
||||
def take_action(self, parsed_args):
|
||||
neutron_client = self.get_client()
|
||||
_speaker_id = get_bgp_speaker_id(neutron_client,
|
||||
parsed_args.bgp_speaker)
|
||||
_peer_id = bgp_peer.get_bgp_peer_id(neutron_client,
|
||||
parsed_args.bgp_peer)
|
||||
neutron_client.add_peer_to_bgp_speaker(_speaker_id,
|
||||
{'bgp_peer_id': _peer_id})
|
||||
print(_('Added BGP peer %(peer)s to BGP speaker %(speaker)s.') %
|
||||
{'peer': parsed_args.bgp_peer,
|
||||
'speaker': parsed_args.bgp_speaker},
|
||||
file=self.app.stdout)
|
||||
|
||||
|
||||
class RemovePeerFromSpeaker(neutronv20.NeutronCommand):
|
||||
"""Remove a peer from the BGP speaker."""
|
||||
|
||||
def get_parser(self, prog_name):
|
||||
parser = super(RemovePeerFromSpeaker, self).get_parser(prog_name)
|
||||
parser.add_argument(
|
||||
'bgp_speaker',
|
||||
metavar='BGP_SPEAKER',
|
||||
help=_('ID or name of the BGP speaker.'))
|
||||
parser.add_argument(
|
||||
'bgp_peer',
|
||||
metavar='BGP_PEER',
|
||||
help=_('ID or name of the BGP peer to remove.'))
|
||||
return parser
|
||||
|
||||
def take_action(self, parsed_args):
|
||||
neutron_client = self.get_client()
|
||||
_speaker_id = get_bgp_speaker_id(neutron_client,
|
||||
parsed_args.bgp_speaker)
|
||||
_peer_id = bgp_peer.get_bgp_peer_id(neutron_client,
|
||||
parsed_args.bgp_peer)
|
||||
neutron_client.remove_peer_from_bgp_speaker(_speaker_id,
|
||||
{'bgp_peer_id': _peer_id})
|
||||
print(_('Removed BGP peer %(peer)s from BGP speaker %(speaker)s.') %
|
||||
{'peer': parsed_args.bgp_peer,
|
||||
'speaker': parsed_args.bgp_speaker},
|
||||
file=self.app.stdout)
|
||||
|
||||
|
||||
class AddNetworkToSpeaker(neutronv20.NeutronCommand):
|
||||
"""Add a network to the BGP speaker."""
|
||||
|
||||
def get_parser(self, prog_name):
|
||||
parser = super(AddNetworkToSpeaker, self).get_parser(prog_name)
|
||||
parser.add_argument(
|
||||
'bgp_speaker',
|
||||
metavar='BGP_SPEAKER',
|
||||
help=_('ID or name of the BGP speaker.'))
|
||||
parser.add_argument(
|
||||
'network',
|
||||
metavar='NETWORK',
|
||||
help=_('ID or name of the network to add.'))
|
||||
return parser
|
||||
|
||||
def take_action(self, parsed_args):
|
||||
neutron_client = self.get_client()
|
||||
_speaker_id = get_bgp_speaker_id(neutron_client,
|
||||
parsed_args.bgp_speaker)
|
||||
_net_id = get_network_id(neutron_client,
|
||||
parsed_args.network)
|
||||
neutron_client.add_network_to_bgp_speaker(_speaker_id,
|
||||
{'network_id': _net_id})
|
||||
print(_('Added network %(net)s to BGP speaker %(speaker)s.') %
|
||||
{'net': parsed_args.network, 'speaker': parsed_args.bgp_speaker},
|
||||
file=self.app.stdout)
|
||||
|
||||
|
||||
class RemoveNetworkFromSpeaker(neutronv20.NeutronCommand):
|
||||
"""Remove a network from the BGP speaker."""
|
||||
|
||||
def get_parser(self, prog_name):
|
||||
parser = super(RemoveNetworkFromSpeaker, self).get_parser(prog_name)
|
||||
parser.add_argument(
|
||||
'bgp_speaker',
|
||||
metavar='BGP_SPEAKER',
|
||||
help=_('ID or name of the BGP speaker.'))
|
||||
parser.add_argument(
|
||||
'network',
|
||||
metavar='NETWORK',
|
||||
help=_('ID or name of the network to remove.'))
|
||||
return parser
|
||||
|
||||
def take_action(self, parsed_args):
|
||||
neutron_client = self.get_client()
|
||||
_speaker_id = get_bgp_speaker_id(neutron_client,
|
||||
parsed_args.bgp_speaker)
|
||||
_net_id = get_network_id(neutron_client,
|
||||
parsed_args.network)
|
||||
neutron_client.remove_network_from_bgp_speaker(_speaker_id,
|
||||
{'network_id': _net_id})
|
||||
print(_('Removed network %(net)s from BGP speaker %(speaker)s.') %
|
||||
{'net': parsed_args.network, 'speaker': parsed_args.bgp_speaker},
|
||||
file=self.app.stdout)
|
||||
|
||||
|
||||
class ListRoutesAdvertisedBySpeaker(neutronv20.ListCommand):
|
||||
"""List routes advertised by a given BGP speaker."""
|
||||
|
||||
list_columns = ['id', 'destination', 'next_hop']
|
||||
resource = 'advertised_route'
|
||||
pagination_support = True
|
||||
sorting_support = True
|
||||
|
||||
def get_parser(self, prog_name):
|
||||
parser = super(ListRoutesAdvertisedBySpeaker,
|
||||
self).get_parser(prog_name)
|
||||
parser.add_argument(
|
||||
'bgp_speaker',
|
||||
metavar='BGP_SPEAKER',
|
||||
help=_('ID or name of the BGP speaker.'))
|
||||
return parser
|
||||
|
||||
def call_server(self, neutron_client, search_opts, parsed_args):
|
||||
_speaker_id = get_bgp_speaker_id(neutron_client,
|
||||
parsed_args.bgp_speaker)
|
||||
data = neutron_client.list_route_advertised_from_bgp_speaker(
|
||||
_speaker_id, **search_opts)
|
||||
return data
|
||||
67
neutronclient/neutron/v2_0/dns.py
Normal file
67
neutronclient/neutron/v2_0/dns.py
Normal file
@@ -0,0 +1,67 @@
|
||||
# Copyright (c) 2016 IBM
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
from neutronclient._i18n import _
|
||||
|
||||
|
||||
def add_dns_argument_create(parser, resource, attribute):
|
||||
# Add dns_name and dns_domain support to network, port and floatingip
|
||||
# create
|
||||
argument = '--dns-%s' % attribute
|
||||
parser.add_argument(
|
||||
argument,
|
||||
help=_('Assign DNS %(attribute)s to the %(resource)s '
|
||||
'(requires DNS integration '
|
||||
'extension)') % {'attribute': attribute, 'resource': resource})
|
||||
|
||||
|
||||
def args2body_dns_create(parsed_args, resource, attribute):
|
||||
# Add dns_name and dns_domain support to network, port and floatingip
|
||||
# create
|
||||
destination = 'dns_%s' % attribute
|
||||
argument = getattr(parsed_args, destination)
|
||||
if argument:
|
||||
resource[destination] = argument
|
||||
|
||||
|
||||
def add_dns_argument_update(parser, resource, attribute):
|
||||
# Add dns_name and dns_domain support to network, port and floatingip
|
||||
# update
|
||||
argument = '--dns-%s' % attribute
|
||||
no_argument = '--no-dns-%s' % attribute
|
||||
dns_args = parser.add_mutually_exclusive_group()
|
||||
dns_args.add_argument(
|
||||
argument,
|
||||
help=_('Assign DNS %(attribute)s to the %(resource)s '
|
||||
'(requires DNS integration '
|
||||
'extension.)') % {'attribute': attribute, 'resource': resource})
|
||||
dns_args.add_argument(
|
||||
no_argument, action='store_true',
|
||||
help=_('Unassign DNS %(attribute)s from the %(resource)s '
|
||||
'(requires DNS integration '
|
||||
'extension.)') % {'attribute': attribute, 'resource': resource})
|
||||
|
||||
|
||||
def args2body_dns_update(parsed_args, resource, attribute):
|
||||
# Add dns_name and dns_domain support to network, port and floatingip
|
||||
# update
|
||||
destination = 'dns_%s' % attribute
|
||||
no_destination = 'no_dns_%s' % attribute
|
||||
argument = getattr(parsed_args, destination)
|
||||
no_argument = getattr(parsed_args, no_destination)
|
||||
if argument:
|
||||
resource[destination] = argument
|
||||
if no_argument:
|
||||
resource[destination] = ""
|
||||
@@ -118,7 +118,7 @@ class AssociateFlavor(neutronV20.NeutronCommand):
|
||||
'flavor.'))
|
||||
return parser
|
||||
|
||||
def run(self, parsed_args):
|
||||
def take_action(self, parsed_args):
|
||||
neutron_client = self.get_client()
|
||||
flavor_id = neutronV20.find_resourceid_by_name_or_id(
|
||||
neutron_client, 'flavor', parsed_args.flavor)
|
||||
@@ -151,7 +151,7 @@ class DisassociateFlavor(neutronV20.NeutronCommand):
|
||||
'flavor.'))
|
||||
return parser
|
||||
|
||||
def run(self, parsed_args):
|
||||
def take_action(self, parsed_args):
|
||||
neutron_client = self.get_client()
|
||||
flavor_id = neutronV20.find_resourceid_by_name_or_id(
|
||||
neutron_client, 'flavor', parsed_args.flavor)
|
||||
|
||||
@@ -20,6 +20,7 @@ import argparse
|
||||
|
||||
from neutronclient._i18n import _
|
||||
from neutronclient.neutron import v2_0 as neutronV20
|
||||
from neutronclient.neutron.v2_0 import dns
|
||||
|
||||
|
||||
class ListFloatingIP(neutronV20.ListCommand):
|
||||
@@ -68,6 +69,8 @@ class CreateFloatingIP(neutronV20.CreateCommand):
|
||||
'--subnet',
|
||||
dest='subnet_id',
|
||||
help=_('Subnet ID on which you want to create the floating IP.'))
|
||||
dns.add_dns_argument_create(parser, self.resource, 'domain')
|
||||
dns.add_dns_argument_create(parser, self.resource, 'name')
|
||||
|
||||
def args2body(self, parsed_args):
|
||||
_network_id = neutronV20.find_resourceid_by_name_or_id(
|
||||
@@ -77,6 +80,8 @@ class CreateFloatingIP(neutronV20.CreateCommand):
|
||||
['port_id', 'tenant_id',
|
||||
'fixed_ip_address',
|
||||
'floating_ip_address', 'subnet_id'])
|
||||
dns.args2body_dns_create(parsed_args, body, 'domain')
|
||||
dns.args2body_dns_create(parsed_args, body, 'name')
|
||||
return {self.resource: body}
|
||||
|
||||
|
||||
@@ -90,7 +95,6 @@ class DeleteFloatingIP(neutronV20.DeleteCommand):
|
||||
class AssociateFloatingIP(neutronV20.NeutronCommand):
|
||||
"""Create a mapping between a floating IP and a fixed IP."""
|
||||
|
||||
api = 'network'
|
||||
resource = 'floatingip'
|
||||
|
||||
def get_parser(self, prog_name):
|
||||
@@ -111,8 +115,7 @@ class AssociateFloatingIP(neutronV20.NeutronCommand):
|
||||
help=argparse.SUPPRESS)
|
||||
return parser
|
||||
|
||||
def run(self, parsed_args):
|
||||
self.log.debug('run(%s)' % parsed_args)
|
||||
def take_action(self, parsed_args):
|
||||
neutron_client = self.get_client()
|
||||
update_dict = {}
|
||||
neutronV20.update_dict(parsed_args, update_dict,
|
||||
@@ -126,7 +129,6 @@ class AssociateFloatingIP(neutronV20.NeutronCommand):
|
||||
class DisassociateFloatingIP(neutronV20.NeutronCommand):
|
||||
"""Remove a mapping from a floating IP to a fixed IP."""
|
||||
|
||||
api = 'network'
|
||||
resource = 'floatingip'
|
||||
|
||||
def get_parser(self, prog_name):
|
||||
@@ -136,8 +138,7 @@ class DisassociateFloatingIP(neutronV20.NeutronCommand):
|
||||
help=_('ID of the floating IP to disassociate.'))
|
||||
return parser
|
||||
|
||||
def run(self, parsed_args):
|
||||
self.log.debug('run(%s)' % parsed_args)
|
||||
def take_action(self, parsed_args):
|
||||
neutron_client = self.get_client()
|
||||
neutron_client.update_floatingip(parsed_args.floatingip_id,
|
||||
{'floatingip': {'port_id': None}})
|
||||
|
||||
@@ -13,11 +13,52 @@
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
#
|
||||
|
||||
from neutronclient._i18n import _
|
||||
from neutronclient.common import utils
|
||||
from neutronclient.neutron import v2_0 as neutronv20
|
||||
|
||||
|
||||
def add_common_args(parser):
|
||||
parser.add_argument(
|
||||
'--name',
|
||||
help=_('Name for the firewall.'))
|
||||
parser.add_argument(
|
||||
'--description',
|
||||
help=_('Description for the firewall.'))
|
||||
router = parser.add_mutually_exclusive_group()
|
||||
router.add_argument(
|
||||
'--router',
|
||||
dest='routers',
|
||||
metavar='ROUTER',
|
||||
action='append',
|
||||
help=_('Firewall associated router name or ID (requires FWaaS '
|
||||
'router insertion extension, this option can be repeated)'))
|
||||
router.add_argument(
|
||||
'--no-routers',
|
||||
action='store_true',
|
||||
help=_('Associate no routers with the firewall (requires FWaaS '
|
||||
'router insertion extension)'))
|
||||
|
||||
|
||||
def parse_common_args(client, parsed_args):
|
||||
body = {}
|
||||
if parsed_args.policy:
|
||||
body['firewall_policy_id'] = neutronv20.find_resourceid_by_name_or_id(
|
||||
client, 'firewall_policy',
|
||||
parsed_args.policy)
|
||||
|
||||
if parsed_args.routers:
|
||||
body['router_ids'] = [
|
||||
neutronv20.find_resourceid_by_name_or_id(client, 'router', r)
|
||||
for r in parsed_args.routers]
|
||||
elif parsed_args.no_routers:
|
||||
body['router_ids'] = []
|
||||
|
||||
neutronv20.update_dict(parsed_args, body,
|
||||
['name', 'description'])
|
||||
return body
|
||||
|
||||
|
||||
class ListFirewall(neutronv20.ListCommand):
|
||||
"""List firewalls that belong to a given tenant."""
|
||||
|
||||
@@ -40,41 +81,20 @@ class CreateFirewall(neutronv20.CreateCommand):
|
||||
resource = 'firewall'
|
||||
|
||||
def add_known_arguments(self, parser):
|
||||
add_common_args(parser)
|
||||
parser.add_argument(
|
||||
'firewall_policy_id', metavar='POLICY',
|
||||
'policy', metavar='POLICY',
|
||||
help=_('Firewall policy name or ID.'))
|
||||
parser.add_argument(
|
||||
'--name',
|
||||
help=_('Name for the firewall.'))
|
||||
parser.add_argument(
|
||||
'--description',
|
||||
help=_('Description for the firewall rule.'))
|
||||
parser.add_argument(
|
||||
'--admin-state-down',
|
||||
dest='admin_state',
|
||||
action='store_false',
|
||||
help=_('Set admin state up to false.'))
|
||||
parser.add_argument(
|
||||
'--router',
|
||||
dest='routers',
|
||||
metavar='ROUTER',
|
||||
action='append',
|
||||
help=_('Firewall associated router names or IDs (requires FWaaS '
|
||||
'router insertion extension, this option can be repeated)'))
|
||||
|
||||
def args2body(self, parsed_args):
|
||||
client = self.get_client()
|
||||
_policy_id = neutronv20.find_resourceid_by_name_or_id(
|
||||
client, 'firewall_policy',
|
||||
parsed_args.firewall_policy_id)
|
||||
body = {'firewall_policy_id': _policy_id,
|
||||
'admin_state_up': parsed_args.admin_state, }
|
||||
if parsed_args.routers:
|
||||
body['router_ids'] = [
|
||||
neutronv20.find_resourceid_by_name_or_id(client, 'router', r)
|
||||
for r in parsed_args.routers]
|
||||
neutronv20.update_dict(parsed_args, body,
|
||||
['name', 'description', 'tenant_id'])
|
||||
body = parse_common_args(self.get_client(), parsed_args)
|
||||
neutronv20.update_dict(parsed_args, body, ['tenant_id'])
|
||||
body['admin_state_up'] = parsed_args.admin_state
|
||||
return {self.resource: body}
|
||||
|
||||
|
||||
@@ -84,38 +104,19 @@ class UpdateFirewall(neutronv20.UpdateCommand):
|
||||
resource = 'firewall'
|
||||
|
||||
def add_known_arguments(self, parser):
|
||||
add_common_args(parser)
|
||||
parser.add_argument(
|
||||
'--policy', metavar='POLICY',
|
||||
help=_('Firewall policy name or ID.'))
|
||||
router_sg = parser.add_mutually_exclusive_group()
|
||||
router_sg.add_argument(
|
||||
'--router',
|
||||
dest='routers',
|
||||
metavar='ROUTER',
|
||||
action='append',
|
||||
help=_('Firewall associated router names or IDs (requires FWaaS '
|
||||
'router insertion extension, this option can be repeated)'))
|
||||
router_sg.add_argument(
|
||||
'--no-routers',
|
||||
action='store_true',
|
||||
help=_('Associate no routers with the firewall (requires FWaaS '
|
||||
'router insertion extension)'))
|
||||
utils.add_boolean_argument(
|
||||
parser, '--admin-state-up', dest='admin_state_up',
|
||||
help=_('Update the admin state for the firewall'
|
||||
'(True means UP)'))
|
||||
|
||||
def args2body(self, parsed_args):
|
||||
data = {}
|
||||
client = self.get_client()
|
||||
if parsed_args.policy:
|
||||
_policy_id = neutronv20.find_resourceid_by_name_or_id(
|
||||
client, 'firewall_policy',
|
||||
parsed_args.policy)
|
||||
data['firewall_policy_id'] = _policy_id
|
||||
if parsed_args.routers:
|
||||
data['router_ids'] = [
|
||||
neutronv20.find_resourceid_by_name_or_id(client, 'router', r)
|
||||
for r in parsed_args.routers]
|
||||
elif parsed_args.no_routers:
|
||||
data['router_ids'] = []
|
||||
return {self.resource: data}
|
||||
body = parse_common_args(self.get_client(), parsed_args)
|
||||
neutronv20.update_dict(parsed_args, body, ['admin_state_up'])
|
||||
return {self.resource: body}
|
||||
|
||||
|
||||
class DeleteFirewall(neutronv20.DeleteCommand):
|
||||
|
||||
@@ -19,6 +19,7 @@ from __future__ import print_function
|
||||
import argparse
|
||||
|
||||
from neutronclient._i18n import _
|
||||
from neutronclient.common import utils
|
||||
from neutronclient.neutron import v2_0 as neutronv20
|
||||
|
||||
|
||||
@@ -31,14 +32,17 @@ def _format_firewall_rules(firewall_policy):
|
||||
return ''
|
||||
|
||||
|
||||
def common_add_known_arguments(parser):
|
||||
def add_common_args(parser):
|
||||
parser.add_argument(
|
||||
'--description',
|
||||
help=_('Description for the firewall policy.'))
|
||||
parser.add_argument(
|
||||
'--firewall-rules', type=lambda x: x.split(),
|
||||
help=_('Ordered list of whitespace-delimited firewall rule '
|
||||
'names or IDs; e.g., --firewall-rules \"rule1 rule2\"'))
|
||||
|
||||
|
||||
def common_args2body(client, parsed_args):
|
||||
def parse_common_args(client, parsed_args):
|
||||
if parsed_args.firewall_rules:
|
||||
_firewall_rules = []
|
||||
for f in parsed_args.firewall_rules:
|
||||
@@ -81,24 +85,20 @@ class CreateFirewallPolicy(neutronv20.CreateCommand):
|
||||
'name',
|
||||
metavar='NAME',
|
||||
help=_('Name for the firewall policy.'))
|
||||
parser.add_argument(
|
||||
'--description',
|
||||
help=_('Description for the firewall policy.'))
|
||||
parser.add_argument(
|
||||
'--shared',
|
||||
dest='shared',
|
||||
action='store_true',
|
||||
help=_('Create a shared policy.'),
|
||||
default=argparse.SUPPRESS)
|
||||
common_add_known_arguments(parser)
|
||||
parser.add_argument(
|
||||
'--audited',
|
||||
action='store_true',
|
||||
help=_('Sets audited to True.'),
|
||||
default=argparse.SUPPRESS)
|
||||
add_common_args(parser)
|
||||
|
||||
def args2body(self, parsed_args):
|
||||
return common_args2body(self.get_client(), parsed_args)
|
||||
return parse_common_args(self.get_client(), parsed_args)
|
||||
|
||||
|
||||
class UpdateFirewallPolicy(neutronv20.UpdateCommand):
|
||||
@@ -107,10 +107,21 @@ class UpdateFirewallPolicy(neutronv20.UpdateCommand):
|
||||
resource = 'firewall_policy'
|
||||
|
||||
def add_known_arguments(self, parser):
|
||||
common_add_known_arguments(parser)
|
||||
add_common_args(parser)
|
||||
parser.add_argument(
|
||||
'--name',
|
||||
help=_('Name for the firewall policy.'))
|
||||
utils.add_boolean_argument(
|
||||
parser, '--shared',
|
||||
help=_('Update the sharing status of the policy. '
|
||||
'(True means shared)'))
|
||||
utils.add_boolean_argument(
|
||||
parser, '--audited',
|
||||
help=_('Update the audit status of the policy. '
|
||||
'(True means auditing is enabled)'))
|
||||
|
||||
def args2body(self, parsed_args):
|
||||
return common_args2body(self.get_client(), parsed_args)
|
||||
return parse_common_args(self.get_client(), parsed_args)
|
||||
|
||||
|
||||
class DeleteFirewallPolicy(neutronv20.DeleteCommand):
|
||||
@@ -168,7 +179,7 @@ class FirewallPolicyInsertRule(neutronv20.UpdateCommand):
|
||||
self.add_known_arguments(parser)
|
||||
return parser
|
||||
|
||||
def run(self, parsed_args):
|
||||
def take_action(self, parsed_args):
|
||||
neutron_client = self.get_client()
|
||||
body = self.args2body(parsed_args)
|
||||
_id = neutronv20.find_resourceid_by_name_or_id(neutron_client,
|
||||
@@ -206,7 +217,7 @@ class FirewallPolicyRemoveRule(neutronv20.UpdateCommand):
|
||||
self.add_known_arguments(parser)
|
||||
return parser
|
||||
|
||||
def run(self, parsed_args):
|
||||
def take_action(self, parsed_args):
|
||||
neutron_client = self.get_client()
|
||||
body = self.args2body(parsed_args)
|
||||
_id = neutronv20.find_resourceid_by_name_or_id(neutron_client,
|
||||
|
||||
@@ -21,6 +21,62 @@ from neutronclient.common import utils
|
||||
from neutronclient.neutron import v2_0 as neutronv20
|
||||
|
||||
|
||||
def _add_common_args(parser, is_create=True):
|
||||
"""If is_create is True, protocol and action become mandatory arguments.
|
||||
|
||||
CreateCommand = is_create : True
|
||||
UpdateCommand = is_create : False
|
||||
"""
|
||||
parser.add_argument(
|
||||
'--name',
|
||||
help=_('Name for the firewall rule.'))
|
||||
parser.add_argument(
|
||||
'--description',
|
||||
help=_('Description for the firewall rule.'))
|
||||
parser.add_argument(
|
||||
'--source-ip-address',
|
||||
help=_('Source IP address or subnet.'))
|
||||
parser.add_argument(
|
||||
'--destination-ip-address',
|
||||
help=_('Destination IP address or subnet.'))
|
||||
parser.add_argument(
|
||||
'--source-port',
|
||||
help=_('Source port (integer in [1, 65535] or range in a:b).'))
|
||||
parser.add_argument(
|
||||
'--destination-port',
|
||||
help=_('Destination port (integer in [1, 65535] or range in '
|
||||
'a:b).'))
|
||||
utils.add_boolean_argument(
|
||||
parser, '--enabled', dest='enabled',
|
||||
help=_('Whether to enable or disable this rule.'))
|
||||
parser.add_argument(
|
||||
'--protocol', choices=['tcp', 'udp', 'icmp', 'any'],
|
||||
required=is_create,
|
||||
type=utils.convert_to_lowercase,
|
||||
help=_('Protocol for the firewall rule.'))
|
||||
parser.add_argument(
|
||||
'--action',
|
||||
required=is_create,
|
||||
type=utils.convert_to_lowercase,
|
||||
choices=['allow', 'deny', 'reject'],
|
||||
help=_('Action for the firewall rule.'))
|
||||
|
||||
|
||||
def common_args2body(parsed_args):
|
||||
body = {}
|
||||
neutronv20.update_dict(parsed_args, body,
|
||||
['name', 'description', 'shared', 'tenant_id',
|
||||
'source_ip_address', 'destination_ip_address',
|
||||
'source_port', 'destination_port', 'action',
|
||||
'enabled', 'ip_version'])
|
||||
protocol = parsed_args.protocol
|
||||
if protocol:
|
||||
if protocol == 'any':
|
||||
protocol = None
|
||||
body['protocol'] = protocol
|
||||
return body
|
||||
|
||||
|
||||
class ListFirewallRule(neutronv20.ListCommand):
|
||||
"""List firewall rules that belong to a given tenant."""
|
||||
|
||||
@@ -69,56 +125,19 @@ class CreateFirewallRule(neutronv20.CreateCommand):
|
||||
resource = 'firewall_rule'
|
||||
|
||||
def add_known_arguments(self, parser):
|
||||
parser.add_argument(
|
||||
'--name',
|
||||
help=_('Name for the firewall rule.'))
|
||||
parser.add_argument(
|
||||
'--description',
|
||||
help=_('Description for the firewall rule.'))
|
||||
parser.add_argument(
|
||||
'--shared',
|
||||
dest='shared',
|
||||
action='store_true',
|
||||
help=_('Set shared to True (default is False).'),
|
||||
help=_('Set shared flag for the firewall rule.'),
|
||||
default=argparse.SUPPRESS)
|
||||
_add_common_args(parser)
|
||||
parser.add_argument(
|
||||
'--source-ip-address',
|
||||
help=_('Source IP address or subnet.'))
|
||||
parser.add_argument(
|
||||
'--destination-ip-address',
|
||||
help=_('Destination IP address or subnet.'))
|
||||
parser.add_argument(
|
||||
'--source-port',
|
||||
help=_('Source port (integer in [1, 65535] or range in a:b).'))
|
||||
parser.add_argument(
|
||||
'--destination-port',
|
||||
help=_('Destination port (integer in [1, 65535] or range in '
|
||||
'a:b).'))
|
||||
utils.add_boolean_argument(
|
||||
parser, '--enabled', dest='enabled',
|
||||
help=_('Whether to enable or disable this rule.'))
|
||||
parser.add_argument(
|
||||
'--protocol', choices=['tcp', 'udp', 'icmp', 'any'],
|
||||
required=True,
|
||||
help=_('Protocol for the firewall rule.'))
|
||||
parser.add_argument(
|
||||
'--action',
|
||||
required=True,
|
||||
choices=['allow', 'deny', 'reject'],
|
||||
help=_('Action for the firewall rule.'))
|
||||
'--ip-version',
|
||||
type=int, choices=[4, 6], default=4,
|
||||
help=_('IP version for the firewall rule (default is 4).'))
|
||||
|
||||
def args2body(self, parsed_args):
|
||||
body = {}
|
||||
neutronv20.update_dict(parsed_args, body,
|
||||
['name', 'description', 'shared', 'protocol',
|
||||
'source_ip_address', 'destination_ip_address',
|
||||
'source_port', 'destination_port',
|
||||
'action', 'enabled', 'tenant_id'])
|
||||
protocol = parsed_args.protocol
|
||||
if protocol == 'any':
|
||||
protocol = None
|
||||
body['protocol'] = protocol
|
||||
return {self.resource: body}
|
||||
return {self.resource: common_args2body(parsed_args)}
|
||||
|
||||
|
||||
class UpdateFirewallRule(neutronv20.UpdateCommand):
|
||||
@@ -127,19 +146,20 @@ class UpdateFirewallRule(neutronv20.UpdateCommand):
|
||||
resource = 'firewall_rule'
|
||||
|
||||
def add_known_arguments(self, parser):
|
||||
utils.add_boolean_argument(
|
||||
parser,
|
||||
'--shared',
|
||||
dest='shared',
|
||||
help=_('Update the shared flag for the firewall rule.'),
|
||||
default=argparse.SUPPRESS)
|
||||
parser.add_argument(
|
||||
'--protocol', choices=['tcp', 'udp', 'icmp', 'any'],
|
||||
required=False,
|
||||
help=_('Protocol for the firewall rule.'))
|
||||
'--ip-version',
|
||||
type=int, choices=[4, 6],
|
||||
help=_('Update IP version for the firewall rule.'))
|
||||
_add_common_args(parser, is_create=False)
|
||||
|
||||
def args2body(self, parsed_args):
|
||||
body = {}
|
||||
protocol = parsed_args.protocol
|
||||
if protocol:
|
||||
if protocol == 'any':
|
||||
protocol = None
|
||||
body['protocol'] = protocol
|
||||
return {self.resource: body}
|
||||
return {self.resource: common_args2body(parsed_args)}
|
||||
|
||||
|
||||
class DeleteFirewallRule(neutronv20.DeleteCommand):
|
||||
|
||||
@@ -124,7 +124,7 @@ class AssociateHealthMonitor(neutronV20.NeutronCommand):
|
||||
help=_('ID of the pool to be associated with the health monitor.'))
|
||||
return parser
|
||||
|
||||
def run(self, parsed_args):
|
||||
def take_action(self, parsed_args):
|
||||
neutron_client = self.get_client()
|
||||
body = {'health_monitor': {'id': parsed_args.health_monitor_id}}
|
||||
pool_id = neutronV20.find_resourceid_by_name_or_id(
|
||||
@@ -150,7 +150,7 @@ class DisassociateHealthMonitor(neutronV20.NeutronCommand):
|
||||
help=_('ID of the pool to be associated with the health monitor.'))
|
||||
return parser
|
||||
|
||||
def run(self, parsed_args):
|
||||
def take_action(self, parsed_args):
|
||||
neutron_client = self.get_client()
|
||||
pool_id = neutronV20.find_resourceid_by_name_or_id(
|
||||
neutron_client, 'pool', parsed_args.pool_id)
|
||||
|
||||
@@ -107,8 +107,7 @@ class RetrievePoolStats(neutronV20.ShowCommand):
|
||||
|
||||
resource = 'pool'
|
||||
|
||||
def get_data(self, parsed_args):
|
||||
self.log.debug('run(%s)' % parsed_args)
|
||||
def take_action(self, parsed_args):
|
||||
neutron_client = self.get_client()
|
||||
pool_id = neutronV20.find_resourceid_by_name_or_id(
|
||||
self.get_client(), 'pool', parsed_args.id)
|
||||
|
||||
155
neutronclient/neutron/v2_0/lb/v2/l7policy.py
Normal file
155
neutronclient/neutron/v2_0/lb/v2/l7policy.py
Normal file
@@ -0,0 +1,155 @@
|
||||
# Copyright 2016 Radware LTD.
|
||||
# All Rights Reserved
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
#
|
||||
|
||||
from neutronclient._i18n import _
|
||||
from neutronclient.common import exceptions
|
||||
from neutronclient.common import utils
|
||||
from neutronclient.neutron import v2_0 as neutronV20
|
||||
|
||||
|
||||
def _get_listener_id(client, listener_id_or_name):
|
||||
return neutronV20.find_resourceid_by_name_or_id(
|
||||
client, 'listener', listener_id_or_name)
|
||||
|
||||
|
||||
def _get_pool_id(client, pool_id_or_name):
|
||||
return neutronV20.find_resourceid_by_name_or_id(
|
||||
client, 'pool', pool_id_or_name, cmd_resource='lbaas_pool')
|
||||
|
||||
|
||||
def _add_common_args(parser, is_create=True):
|
||||
parser.add_argument(
|
||||
'--name',
|
||||
help=_('Name of the policy.'))
|
||||
parser.add_argument(
|
||||
'--description',
|
||||
help=_('Description of the policy.'))
|
||||
parser.add_argument(
|
||||
'--action',
|
||||
required=is_create,
|
||||
metavar='ACTION',
|
||||
type=utils.convert_to_uppercase,
|
||||
choices=['REJECT', 'REDIRECT_TO_POOL', 'REDIRECT_TO_URL'],
|
||||
help=_('Action type of the policy.'))
|
||||
parser.add_argument(
|
||||
'--redirect-pool',
|
||||
help=_('ID or name of the pool for REDIRECT_TO_POOL action type.'))
|
||||
parser.add_argument(
|
||||
'--redirect-url',
|
||||
help=_('URL for REDIRECT_TO_URL action type. '
|
||||
'This should be a valid URL string.'))
|
||||
parser.add_argument(
|
||||
'--position',
|
||||
type=int,
|
||||
help=_('L7 policy position in ordered policies list. '
|
||||
'This must be an integer starting from 1. '
|
||||
'Not specifying the position will place the policy '
|
||||
'at the tail of existing policies list.'))
|
||||
|
||||
|
||||
def _common_args2body(client, parsed_args, is_create=True):
|
||||
if parsed_args.redirect_url:
|
||||
if parsed_args.action != 'REDIRECT_TO_URL':
|
||||
raise exceptions.CommandError(_('Action must be REDIRECT_TO_URL'))
|
||||
if parsed_args.redirect_pool:
|
||||
if parsed_args.action != 'REDIRECT_TO_POOL':
|
||||
raise exceptions.CommandError(_('Action must be REDIRECT_TO_POOL'))
|
||||
parsed_args.redirect_pool_id = _get_pool_id(
|
||||
client, parsed_args.redirect_pool)
|
||||
if (parsed_args.action == 'REDIRECT_TO_URL' and
|
||||
not parsed_args.redirect_url):
|
||||
raise exceptions.CommandError(_('Redirect URL must be specified'))
|
||||
if (parsed_args.action == 'REDIRECT_TO_POOL' and
|
||||
not parsed_args.redirect_pool):
|
||||
raise exceptions.CommandError(_('Redirect pool must be specified'))
|
||||
|
||||
attributes = ['name', 'description',
|
||||
'action', 'redirect_pool_id', 'redirect_url',
|
||||
'position', 'admin_state_up']
|
||||
if is_create:
|
||||
parsed_args.listener_id = _get_listener_id(
|
||||
client, parsed_args.listener)
|
||||
attributes.extend(['listener_id', 'tenant_id'])
|
||||
body = {}
|
||||
neutronV20.update_dict(parsed_args, body, attributes)
|
||||
return {'l7policy': body}
|
||||
|
||||
|
||||
class ListL7Policy(neutronV20.ListCommand):
|
||||
"""LBaaS v2 List L7 policies that belong to a given listener."""
|
||||
|
||||
resource = 'l7policy'
|
||||
shadow_resource = 'lbaas_l7policy'
|
||||
pagination_support = True
|
||||
sorting_support = True
|
||||
list_columns = [
|
||||
'id', 'name', 'action', 'redirect_pool_id', 'redirect_url',
|
||||
'position', 'admin_state_up', 'status'
|
||||
]
|
||||
|
||||
|
||||
class ShowL7Policy(neutronV20.ShowCommand):
|
||||
"""LBaaS v2 Show information of a given L7 policy."""
|
||||
|
||||
resource = 'l7policy'
|
||||
shadow_resource = 'lbaas_l7policy'
|
||||
|
||||
|
||||
class CreateL7Policy(neutronV20.CreateCommand):
|
||||
"""LBaaS v2 Create L7 policy."""
|
||||
|
||||
resource = 'l7policy'
|
||||
shadow_resource = 'lbaas_l7policy'
|
||||
|
||||
def add_known_arguments(self, parser):
|
||||
_add_common_args(parser)
|
||||
parser.add_argument(
|
||||
'--admin-state-down',
|
||||
dest='admin_state_up',
|
||||
action='store_false',
|
||||
help=_('Set admin state up to false.'))
|
||||
parser.add_argument(
|
||||
'--listener',
|
||||
required=True,
|
||||
metavar='LISTENER',
|
||||
help=_('ID or name of the listener this policy belongs to.'))
|
||||
|
||||
def args2body(self, parsed_args):
|
||||
return _common_args2body(self.get_client(), parsed_args)
|
||||
|
||||
|
||||
class UpdateL7Policy(neutronV20.UpdateCommand):
|
||||
"""LBaaS v2 Update a given L7 policy."""
|
||||
|
||||
resource = 'l7policy'
|
||||
shadow_resource = 'lbaas_l7policy'
|
||||
|
||||
def add_known_arguments(self, parser):
|
||||
_add_common_args(parser, is_create=False)
|
||||
utils.add_boolean_argument(
|
||||
parser, '--admin-state-up',
|
||||
help=_('Specify the administrative state of the policy'
|
||||
' (True meaning "Up").'))
|
||||
|
||||
def args2body(self, parsed_args):
|
||||
return _common_args2body(self.get_client(), parsed_args, False)
|
||||
|
||||
|
||||
class DeleteL7Policy(neutronV20.DeleteCommand):
|
||||
"""LBaaS v2 Delete a given L7 policy."""
|
||||
|
||||
resource = 'l7policy'
|
||||
shadow_resource = 'lbaas_l7policy'
|
||||
148
neutronclient/neutron/v2_0/lb/v2/l7rule.py
Normal file
148
neutronclient/neutron/v2_0/lb/v2/l7rule.py
Normal file
@@ -0,0 +1,148 @@
|
||||
# Copyright 2016 Radware LTD.
|
||||
# All Rights Reserved
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
#
|
||||
|
||||
|
||||
from neutronclient._i18n import _
|
||||
from neutronclient.common import utils
|
||||
from neutronclient.neutron import v2_0 as neutronV20
|
||||
|
||||
|
||||
def _get_policy_id(client, policy_id_or_name):
|
||||
return neutronV20.find_resourceid_by_name_or_id(
|
||||
client, 'l7policy', policy_id_or_name,
|
||||
cmd_resource='lbaas_l7policy')
|
||||
|
||||
|
||||
class LbaasL7RuleMixin(object):
|
||||
|
||||
def set_extra_attrs(self, parsed_args):
|
||||
self.parent_id = _get_policy_id(self.get_client(),
|
||||
parsed_args.l7policy)
|
||||
|
||||
def add_known_arguments(self, parser):
|
||||
parser.add_argument(
|
||||
'l7policy', metavar='L7POLICY',
|
||||
help=_('ID or name of L7 policy this rule belongs to.'))
|
||||
|
||||
|
||||
def _add_common_args(parser, is_create=True):
|
||||
parser.add_argument(
|
||||
'--type',
|
||||
required=is_create,
|
||||
type=utils.convert_to_uppercase,
|
||||
choices=['HOST_NAME', 'PATH', 'FILE_TYPE', 'HEADER', 'COOKIE'],
|
||||
help=_('Rule type.'))
|
||||
parser.add_argument(
|
||||
'--compare-type',
|
||||
required=is_create,
|
||||
type=utils.convert_to_uppercase,
|
||||
choices=['REGEX', 'STARTS_WITH', 'ENDS_WITH',
|
||||
'CONTAINS', 'EQUAL_TO'],
|
||||
help=_('Rule compare type.'))
|
||||
parser.add_argument(
|
||||
'--invert-compare',
|
||||
dest='invert',
|
||||
action='store_true',
|
||||
help=_('Invert the compare type.'))
|
||||
parser.add_argument(
|
||||
'--key',
|
||||
help=_('Key to compare.'
|
||||
' Relevant for HEADER and COOKIE types only.'))
|
||||
parser.add_argument(
|
||||
'--value',
|
||||
required=is_create,
|
||||
help=_('Value to compare.'))
|
||||
|
||||
|
||||
def _common_args2body(client, parsed_args, is_create=True):
|
||||
attributes = ['type', 'compare_type',
|
||||
'invert', 'key', 'value', 'admin_state_up']
|
||||
if is_create:
|
||||
attributes.append('tenant_id')
|
||||
body = {}
|
||||
neutronV20.update_dict(parsed_args, body, attributes)
|
||||
return {'rule': body}
|
||||
|
||||
|
||||
class ListL7Rule(LbaasL7RuleMixin, neutronV20.ListCommand):
|
||||
"""LBaaS v2 List L7 rules that belong to a given L7 policy."""
|
||||
|
||||
resource = 'rule'
|
||||
shadow_resource = 'lbaas_l7rule'
|
||||
pagination_support = True
|
||||
sorting_support = True
|
||||
|
||||
list_columns = [
|
||||
'id', 'type', 'compare_type', 'invert', 'key', 'value',
|
||||
'admin_state_up', 'status'
|
||||
]
|
||||
|
||||
def take_action(self, parsed_args):
|
||||
self.parent_id = _get_policy_id(self.get_client(),
|
||||
parsed_args.l7policy)
|
||||
self.values_specs.append('--l7policy_id=%s' % self.parent_id)
|
||||
return super(ListL7Rule, self).take_action(parsed_args)
|
||||
|
||||
|
||||
class ShowL7Rule(LbaasL7RuleMixin, neutronV20.ShowCommand):
|
||||
"""LBaaS v2 Show information of a given rule."""
|
||||
|
||||
resource = 'rule'
|
||||
shadow_resource = 'lbaas_l7rule'
|
||||
|
||||
|
||||
class CreateL7Rule(LbaasL7RuleMixin, neutronV20.CreateCommand):
|
||||
"""LBaaS v2 Create L7 rule."""
|
||||
|
||||
resource = 'rule'
|
||||
shadow_resource = 'lbaas_l7rule'
|
||||
|
||||
def add_known_arguments(self, parser):
|
||||
super(CreateL7Rule, self).add_known_arguments(parser)
|
||||
_add_common_args(parser)
|
||||
parser.add_argument(
|
||||
'--admin-state-down',
|
||||
dest='admin_state_up',
|
||||
action='store_false',
|
||||
help=_('Set admin state up to false'))
|
||||
|
||||
def args2body(self, parsed_args):
|
||||
return _common_args2body(self.get_client(), parsed_args)
|
||||
|
||||
|
||||
class UpdateL7Rule(LbaasL7RuleMixin, neutronV20.UpdateCommand):
|
||||
"""LBaaS v2 Update a given L7 rule."""
|
||||
|
||||
resource = 'rule'
|
||||
shadow_resource = 'lbaas_l7rule'
|
||||
|
||||
def add_known_arguments(self, parser):
|
||||
super(UpdateL7Rule, self).add_known_arguments(parser)
|
||||
_add_common_args(parser, False)
|
||||
utils.add_boolean_argument(
|
||||
parser, '--admin-state-up',
|
||||
help=_('Specify the administrative state of the rule'
|
||||
' (True meaning "Up").'))
|
||||
|
||||
def args2body(self, parsed_args):
|
||||
return _common_args2body(self.get_client(), parsed_args, False)
|
||||
|
||||
|
||||
class DeleteL7Rule(LbaasL7RuleMixin, neutronV20.DeleteCommand):
|
||||
"""LBaaS v2 Delete a given L7 rule."""
|
||||
|
||||
resource = 'rule'
|
||||
shadow_resource = 'lbaas_l7rule'
|
||||
@@ -16,18 +16,27 @@
|
||||
#
|
||||
|
||||
from neutronclient._i18n import _
|
||||
from neutronclient.common import exceptions
|
||||
from neutronclient.common import utils
|
||||
from neutronclient.neutron import v2_0 as neutronV20
|
||||
|
||||
|
||||
def _get_loadbalancer_id(client, lb_id_or_name):
|
||||
return neutronV20.find_resourceid_by_name_or_id(
|
||||
client,
|
||||
'loadbalancer',
|
||||
lb_id_or_name,
|
||||
client, 'loadbalancer', lb_id_or_name,
|
||||
cmd_resource='lbaas_loadbalancer')
|
||||
|
||||
|
||||
def _get_pool(client, pool_id_or_name):
|
||||
return neutronV20.find_resource_by_name_or_id(
|
||||
client, 'pool', pool_id_or_name, cmd_resource='lbaas_pool')
|
||||
|
||||
|
||||
def _get_pool_id(client, pool_id_or_name):
|
||||
return neutronV20.find_resourceid_by_name_or_id(
|
||||
client, 'pool', pool_id_or_name, cmd_resource='lbaas_pool')
|
||||
|
||||
|
||||
class ListListener(neutronV20.ListCommand):
|
||||
"""LBaaS v2 List listeners that belong to a given tenant."""
|
||||
|
||||
@@ -64,7 +73,8 @@ class CreateListener(neutronV20.CreateCommand):
|
||||
help=_('Description of the listener.'))
|
||||
parser.add_argument(
|
||||
'--name',
|
||||
help=_('The name of the listener.'))
|
||||
help=_('The name of the listener. At least one of --default-pool '
|
||||
'or --loadbalancer must be specified.'))
|
||||
parser.add_argument(
|
||||
'--default-tls-container-ref',
|
||||
dest='default_tls_container_ref',
|
||||
@@ -75,9 +85,11 @@ class CreateListener(neutronV20.CreateCommand):
|
||||
dest='sni_container_refs',
|
||||
nargs='+',
|
||||
help=_('List of TLS container references for SNI.'))
|
||||
parser.add_argument(
|
||||
'--default-pool',
|
||||
help=_('Default pool for the listener.'))
|
||||
parser.add_argument(
|
||||
'--loadbalancer',
|
||||
required=True,
|
||||
metavar='LOADBALANCER',
|
||||
help=_('ID or name of the load balancer.'))
|
||||
parser.add_argument(
|
||||
@@ -93,22 +105,29 @@ class CreateListener(neutronV20.CreateCommand):
|
||||
help=_('Protocol port for the listener.'))
|
||||
|
||||
def args2body(self, parsed_args):
|
||||
resource = {
|
||||
'protocol': parsed_args.protocol,
|
||||
'protocol_port': parsed_args.protocol_port,
|
||||
'admin_state_up': parsed_args.admin_state
|
||||
}
|
||||
if not parsed_args.loadbalancer and not parsed_args.default_pool:
|
||||
message = _('Either --default-pool or --loadbalancer must be '
|
||||
'specified.')
|
||||
raise exceptions.CommandError(message)
|
||||
if parsed_args.loadbalancer:
|
||||
parsed_args.loadbalancer = _get_loadbalancer_id(
|
||||
self.get_client(),
|
||||
parsed_args.loadbalancer)
|
||||
body = {'loadbalancer_id': parsed_args.loadbalancer,
|
||||
'protocol': parsed_args.protocol,
|
||||
'protocol_port': parsed_args.protocol_port,
|
||||
'admin_state_up': parsed_args.admin_state}
|
||||
loadbalancer_id = _get_loadbalancer_id(
|
||||
self.get_client(), parsed_args.loadbalancer)
|
||||
resource['loadbalancer_id'] = loadbalancer_id
|
||||
if parsed_args.default_pool:
|
||||
default_pool_id = _get_pool_id(
|
||||
self.get_client(), parsed_args.default_pool)
|
||||
resource['default_pool_id'] = default_pool_id
|
||||
|
||||
neutronV20.update_dict(parsed_args, body,
|
||||
neutronV20.update_dict(parsed_args, resource,
|
||||
['connection_limit', 'description',
|
||||
'loadbalancer_id', 'name',
|
||||
'default_tls_container_ref',
|
||||
'sni_container_refs',
|
||||
'tenant_id'])
|
||||
return {self.resource: body}
|
||||
'name', 'default_tls_container_ref',
|
||||
'sni_container_refs', 'tenant_id'])
|
||||
return {self.resource: resource}
|
||||
|
||||
|
||||
class UpdateListener(neutronV20.UpdateCommand):
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
#
|
||||
from oslo_serialization import jsonutils
|
||||
|
||||
from neutronclient._i18n import _
|
||||
from neutronclient.neutron import v2_0 as neutronV20
|
||||
@@ -100,8 +101,7 @@ class RetrieveLoadBalancerStats(neutronV20.ShowCommand):
|
||||
|
||||
resource = 'loadbalancer'
|
||||
|
||||
def get_data(self, parsed_args):
|
||||
self.log.debug('run(%s)' % parsed_args)
|
||||
def take_action(self, parsed_args):
|
||||
neutron_client = self.get_client()
|
||||
neutron_client.format = parsed_args.request_format
|
||||
loadbalancer_id = neutronV20.find_resourceid_by_name_or_id(
|
||||
@@ -128,3 +128,31 @@ class RetrieveLoadBalancerStats(neutronV20.ShowCommand):
|
||||
# here covert the data dict to the 1-1 vector format below:
|
||||
# [(field1, field2, field3, ...), (value1, value2, value3, ...)]
|
||||
return list(zip(*sorted(stats.items())))
|
||||
|
||||
|
||||
class RetrieveLoadBalancerStatus(neutronV20.NeutronCommand):
|
||||
"""Retrieve status for a given loadbalancer.
|
||||
|
||||
The only output is a formatted JSON tree, and the table format
|
||||
does not support this type of data.
|
||||
"""
|
||||
resource = 'loadbalancer'
|
||||
|
||||
def get_parser(self, prog_name):
|
||||
parser = super(RetrieveLoadBalancerStatus, self).get_parser(prog_name)
|
||||
parser.add_argument(
|
||||
self.resource, metavar=self.resource.upper(),
|
||||
help=_('ID or name of %s to show.') % self.resource)
|
||||
|
||||
return parser
|
||||
|
||||
def take_action(self, parsed_args):
|
||||
self.log.debug('run(%s)' % parsed_args)
|
||||
neutron_client = self.get_client()
|
||||
lb_id = neutronV20.find_resourceid_by_name_or_id(
|
||||
neutron_client, self.resource, parsed_args.loadbalancer)
|
||||
params = {}
|
||||
data = neutron_client.retrieve_loadbalancer_status(lb_id, **params)
|
||||
res = data['statuses']
|
||||
if 'statuses' in data:
|
||||
print(jsonutils.dumps(res, indent=4))
|
||||
|
||||
@@ -49,10 +49,10 @@ class ListMember(LbaasMemberMixin, neutronV20.ListCommand):
|
||||
pagination_support = True
|
||||
sorting_support = True
|
||||
|
||||
def get_data(self, parsed_args):
|
||||
def take_action(self, parsed_args):
|
||||
self.parent_id = _get_pool_id(self.get_client(), parsed_args.pool)
|
||||
self.values_specs.append('--pool_id=%s' % self.parent_id)
|
||||
return super(ListMember, self).get_data(parsed_args)
|
||||
return super(ListMember, self).take_action(parsed_args)
|
||||
|
||||
|
||||
class ShowMember(LbaasMemberMixin, neutronV20.ShowCommand):
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
# Copyright 2013 Mirantis Inc.
|
||||
# Copyright 2014 Blue Box Group, Inc.
|
||||
# Copyright 2015 Hewlett-Packard Development Company, L.P.
|
||||
# Copyright 2015 Blue Box, an IBM Company
|
||||
# All Rights Reserved
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
@@ -17,10 +18,27 @@
|
||||
#
|
||||
|
||||
from neutronclient._i18n import _
|
||||
from neutronclient.common import exceptions
|
||||
from neutronclient.common import utils
|
||||
from neutronclient.neutron import v2_0 as neutronV20
|
||||
|
||||
|
||||
def _get_loadbalancer_id(client, lb_id_or_name):
|
||||
return neutronV20.find_resourceid_by_name_or_id(
|
||||
client, 'loadbalancer', lb_id_or_name,
|
||||
cmd_resource='lbaas_loadbalancer')
|
||||
|
||||
|
||||
def _get_listener(client, listener_id_or_name):
|
||||
return neutronV20.find_resource_by_name_or_id(
|
||||
client, 'listener', listener_id_or_name)
|
||||
|
||||
|
||||
def _get_listener_id(client, listener_id_or_name):
|
||||
return neutronV20.find_resourceid_by_name_or_id(
|
||||
client, 'listener', listener_id_or_name)
|
||||
|
||||
|
||||
class ListPool(neutronV20.ListCommand):
|
||||
"""LBaaS v2 List pools that belong to a given tenant."""
|
||||
|
||||
@@ -64,20 +82,28 @@ class CreatePool(neutronV20.CreateCommand):
|
||||
parser.add_argument(
|
||||
'--session-persistence',
|
||||
metavar='type=TYPE[,cookie_name=COOKIE_NAME]',
|
||||
type=utils.str2dict_type(required_keys=['type'],
|
||||
optional_keys=['cookie_name']),
|
||||
help=_('The type of session persistence to use and associated '
|
||||
'cookie name'))
|
||||
parser.add_argument(
|
||||
'--name', help=_('The name of the pool.'))
|
||||
parser.add_argument(
|
||||
'--listener',
|
||||
help=_('Listener whose default-pool should be set to this pool. '
|
||||
'At least one of --listener or --loadbalancer must be '
|
||||
'specified.'))
|
||||
parser.add_argument(
|
||||
'--loadbalancer',
|
||||
help=_('Loadbalancer with which this pool should be associated. '
|
||||
'At least one of --listener or --loadbalancer must be '
|
||||
'specified.'))
|
||||
parser.add_argument(
|
||||
'--lb-algorithm',
|
||||
required=True,
|
||||
choices=['ROUND_ROBIN', 'LEAST_CONNECTIONS', 'SOURCE_IP'],
|
||||
help=_('The algorithm used to distribute load between the members '
|
||||
'of the pool.'))
|
||||
parser.add_argument(
|
||||
'--listener',
|
||||
required=True,
|
||||
help=_('The listener to associate with the pool'))
|
||||
parser.add_argument(
|
||||
'--protocol',
|
||||
required=True,
|
||||
@@ -86,19 +112,29 @@ class CreatePool(neutronV20.CreateCommand):
|
||||
help=_('Protocol for balancing.'))
|
||||
|
||||
def args2body(self, parsed_args):
|
||||
if parsed_args.session_persistence:
|
||||
parsed_args.session_persistence = utils.str2dict(
|
||||
parsed_args.session_persistence)
|
||||
_listener_id = neutronV20.find_resourceid_by_name_or_id(
|
||||
self.get_client(), 'listener', parsed_args.listener)
|
||||
body = {'admin_state_up': parsed_args.admin_state,
|
||||
'protocol': parsed_args.protocol,
|
||||
'lb_algorithm': parsed_args.lb_algorithm,
|
||||
'listener_id': _listener_id}
|
||||
neutronV20.update_dict(parsed_args, body,
|
||||
resource = {
|
||||
'admin_state_up': parsed_args.admin_state,
|
||||
'protocol': parsed_args.protocol,
|
||||
'lb_algorithm': parsed_args.lb_algorithm
|
||||
}
|
||||
if not parsed_args.listener and not parsed_args.loadbalancer:
|
||||
message = _('At least one of --listener or --loadbalancer must be '
|
||||
'specified.')
|
||||
raise exceptions.CommandError(message)
|
||||
if parsed_args.listener:
|
||||
listener_id = _get_listener_id(
|
||||
self.get_client(),
|
||||
parsed_args.listener)
|
||||
resource['listener_id'] = listener_id
|
||||
if parsed_args.loadbalancer:
|
||||
loadbalancer_id = _get_loadbalancer_id(
|
||||
self.get_client(),
|
||||
parsed_args.loadbalancer)
|
||||
resource['loadbalancer_id'] = loadbalancer_id
|
||||
neutronV20.update_dict(parsed_args, resource,
|
||||
['description', 'name',
|
||||
'session_persistence', 'tenant_id'])
|
||||
return {self.resource: body}
|
||||
return {self.resource: resource}
|
||||
|
||||
|
||||
class UpdatePool(neutronV20.UpdateCommand):
|
||||
|
||||
@@ -21,6 +21,7 @@ from neutronclient.common import exceptions
|
||||
from neutronclient.common import utils
|
||||
from neutronclient.neutron import v2_0 as neutronV20
|
||||
from neutronclient.neutron.v2_0 import availability_zone
|
||||
from neutronclient.neutron.v2_0 import dns
|
||||
from neutronclient.neutron.v2_0.qos import policy as qos_policy
|
||||
|
||||
|
||||
@@ -44,6 +45,44 @@ class ListNetwork(neutronV20.ListCommand):
|
||||
pagination_support = True
|
||||
sorting_support = True
|
||||
|
||||
filter_attrs = [
|
||||
'tenant_id',
|
||||
'name',
|
||||
'admin_state_up',
|
||||
{'name': 'status',
|
||||
'help': _("Filter %s according to their operation status."
|
||||
"(For example: ACTIVE, ERROR etc)"),
|
||||
'boolean': False,
|
||||
'argparse_kwargs': {'type': utils.convert_to_uppercase}},
|
||||
{'name': 'shared',
|
||||
'help': _('Filter and list the networks which are shared.'),
|
||||
'boolean': True},
|
||||
{'name': 'router:external',
|
||||
'help': _('Filter and list the networks which are external.'),
|
||||
'boolean': True},
|
||||
{'name': 'tags',
|
||||
'help': _("Filter and list %s which has all given tags. "
|
||||
"Multiple tags can be set like --tags <tag[,tag...]>"),
|
||||
'boolean': False,
|
||||
'argparse_kwargs': {'metavar': 'TAG'}},
|
||||
{'name': 'tags_any',
|
||||
'help': _("Filter and list %s which has any given tags. "
|
||||
"Multiple tags can be set like --tags-any <tag[,tag...]>"),
|
||||
'boolean': False,
|
||||
'argparse_kwargs': {'metavar': 'TAG'}},
|
||||
{'name': 'not_tags',
|
||||
'help': _("Filter and list %s which does not have all given tags. "
|
||||
"Multiple tags can be set like --not-tags <tag[,tag...]>"),
|
||||
'boolean': False,
|
||||
'argparse_kwargs': {'metavar': 'TAG'}},
|
||||
{'name': 'not_tags_any',
|
||||
'help': _("Filter and list %s which does not have any given tags. "
|
||||
"Multiple tags can be set like --not-tags-any "
|
||||
"<tag[,tag...]>"),
|
||||
'boolean': False,
|
||||
'argparse_kwargs': {'metavar': 'TAG'}},
|
||||
]
|
||||
|
||||
def extend_list(self, data, parsed_args):
|
||||
"""Add subnet information to a network list."""
|
||||
neutron_client = self.get_client()
|
||||
@@ -148,6 +187,7 @@ class CreateNetwork(neutronV20.CreateCommand, qos_policy.CreateQosPolicyMixin):
|
||||
|
||||
self.add_arguments_qos_policy(parser)
|
||||
availability_zone.add_az_hint_argument(parser, self.resource)
|
||||
dns.add_dns_argument_create(parser, self.resource, 'domain')
|
||||
|
||||
def args2body(self, parsed_args):
|
||||
body = {'name': parsed_args.name,
|
||||
@@ -161,6 +201,7 @@ class CreateNetwork(neutronV20.CreateCommand, qos_policy.CreateQosPolicyMixin):
|
||||
|
||||
self.args2body_qos_policy(parsed_args, body)
|
||||
availability_zone.args2body_az_hint(parsed_args, body)
|
||||
dns.args2body_dns_create(parsed_args, body, 'domain')
|
||||
|
||||
return {'network': body}
|
||||
|
||||
@@ -178,8 +219,10 @@ class UpdateNetwork(neutronV20.UpdateCommand, qos_policy.UpdateQosPolicyMixin):
|
||||
|
||||
def add_known_arguments(self, parser):
|
||||
self.add_arguments_qos_policy(parser)
|
||||
dns.add_dns_argument_update(parser, self.resource, 'domain')
|
||||
|
||||
def args2body(self, parsed_args):
|
||||
body = {}
|
||||
self.args2body_qos_policy(parsed_args, body)
|
||||
dns.args2body_dns_update(parsed_args, body, 'domain')
|
||||
return {'network': body}
|
||||
|
||||
73
neutronclient/neutron/v2_0/network_ip_availability.py
Normal file
73
neutronclient/neutron/v2_0/network_ip_availability.py
Normal file
@@ -0,0 +1,73 @@
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
#
|
||||
|
||||
from cliff import show
|
||||
import six
|
||||
|
||||
from neutronclient._i18n import _
|
||||
from neutronclient.neutron import v2_0 as neutronV20
|
||||
|
||||
|
||||
class ListIpAvailability(neutronV20.ListCommand):
|
||||
"""List IP usage of networks"""
|
||||
|
||||
resource = 'network_ip_availability'
|
||||
resource_plural = 'network_ip_availabilities'
|
||||
list_columns = ['network_id', 'network_name', 'total_ips', 'used_ips']
|
||||
paginations_support = True
|
||||
sorting_support = True
|
||||
|
||||
filter_attrs = [
|
||||
{'name': 'ip_version',
|
||||
'help': _('Returns IP availability for the network subnets '
|
||||
'with a given IP version. Default: 4'),
|
||||
'argparse_kwargs': {'type': int,
|
||||
'choices': [4, 6],
|
||||
'default': 4}
|
||||
},
|
||||
{'name': 'network_id',
|
||||
'help': _('Returns IP availability for the network '
|
||||
'matching a given network ID.')},
|
||||
{'name': 'network_name',
|
||||
'help': _('Returns IP availability for the network '
|
||||
'matching a given name.')},
|
||||
{'name': 'tenant_id',
|
||||
'help': _('Returns IP availability for the networks '
|
||||
'with a given tenant ID.')},
|
||||
]
|
||||
|
||||
|
||||
class ShowIpAvailability(neutronV20.NeutronCommand, show.ShowOne):
|
||||
"""Show IP usage of specific network"""
|
||||
|
||||
resource = 'network_ip_availability'
|
||||
|
||||
def get_parser(self, prog_name):
|
||||
parser = super(ShowIpAvailability, self).get_parser(prog_name)
|
||||
parser.add_argument(
|
||||
'network_id', metavar='NETWORK',
|
||||
help=_('ID or name of network to look up.'))
|
||||
return parser
|
||||
|
||||
def take_action(self, parsed_args):
|
||||
self.log.debug('run(%s)', parsed_args)
|
||||
neutron_client = self.get_client()
|
||||
_id = neutronV20.find_resourceid_by_name_or_id(
|
||||
neutron_client, 'network', parsed_args.network_id)
|
||||
data = neutron_client.show_network_ip_availability(_id)
|
||||
self.format_output_data(data)
|
||||
resource = data[self.resource]
|
||||
if self.resource in data:
|
||||
return zip(*sorted(six.iteritems(resource)))
|
||||
else:
|
||||
return None
|
||||
@@ -234,8 +234,7 @@ class NetworkGatewayInterfaceCommand(neutronV20.NeutronCommand):
|
||||
class ConnectNetworkGateway(NetworkGatewayInterfaceCommand):
|
||||
"""Add an internal network interface to a router."""
|
||||
|
||||
def run(self, parsed_args):
|
||||
self.log.debug('run(%s)' % parsed_args)
|
||||
def take_action(self, parsed_args):
|
||||
neutron_client = self.get_client()
|
||||
(gateway_id, network_id) = self.retrieve_ids(neutron_client,
|
||||
parsed_args)
|
||||
@@ -252,8 +251,7 @@ class ConnectNetworkGateway(NetworkGatewayInterfaceCommand):
|
||||
class DisconnectNetworkGateway(NetworkGatewayInterfaceCommand):
|
||||
"""Remove a network from a network gateway."""
|
||||
|
||||
def run(self, parsed_args):
|
||||
self.log.debug('run(%s)' % parsed_args)
|
||||
def take_action(self, parsed_args):
|
||||
neutron_client = self.get_client()
|
||||
(gateway_id, network_id) = self.retrieve_ids(neutron_client,
|
||||
parsed_args)
|
||||
|
||||
@@ -22,6 +22,7 @@ from neutronclient._i18n import _
|
||||
from neutronclient.common import exceptions
|
||||
from neutronclient.common import utils
|
||||
from neutronclient.neutron import v2_0 as neutronV20
|
||||
from neutronclient.neutron.v2_0 import dns
|
||||
from neutronclient.neutron.v2_0.qos import policy as qos_policy
|
||||
|
||||
|
||||
@@ -46,6 +47,7 @@ def _add_updatable_args(parser):
|
||||
parser.add_argument(
|
||||
'--fixed-ip', metavar='subnet_id=SUBNET,ip_address=IP_ADDR',
|
||||
action='append',
|
||||
type=utils.str2dict_type(optional_keys=['subnet_id', 'ip_address']),
|
||||
help=_('Desired IP and/or subnet for this port: '
|
||||
'subnet_id=<name_or_id>,ip_address=<ip>. '
|
||||
'You can repeat this option.'))
|
||||
@@ -73,13 +75,12 @@ def _updatable_args2body(parsed_args, body, client):
|
||||
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']
|
||||
if 'subnet_id' in ip_spec:
|
||||
subnet_name_id = ip_spec['subnet_id']
|
||||
_subnet_id = neutronV20.find_resourceid_by_name_or_id(
|
||||
client, 'subnet', subnet_name_id)
|
||||
ip_dict['subnet_id'] = _subnet_id
|
||||
ips.append(ip_dict)
|
||||
ip_spec['subnet_id'] = _subnet_id
|
||||
ips.append(ip_spec)
|
||||
if ips:
|
||||
body['fixed_ips'] = ips
|
||||
|
||||
@@ -107,16 +108,16 @@ class ListRouterPort(neutronV20.ListCommand):
|
||||
def get_parser(self, prog_name):
|
||||
parser = super(ListRouterPort, self).get_parser(prog_name)
|
||||
parser.add_argument(
|
||||
'id', metavar='router',
|
||||
'id', metavar='ROUTER',
|
||||
help=_('ID or name of router to look up.'))
|
||||
return parser
|
||||
|
||||
def get_data(self, parsed_args):
|
||||
def take_action(self, parsed_args):
|
||||
neutron_client = self.get_client()
|
||||
_id = neutronV20.find_resourceid_by_name_or_id(
|
||||
neutron_client, 'router', parsed_args.id)
|
||||
self.values_specs.append('--device_id=%s' % _id)
|
||||
return super(ListRouterPort, self).get_data(parsed_args)
|
||||
return super(ListRouterPort, self).take_action(parsed_args)
|
||||
|
||||
|
||||
class ShowPort(neutronV20.ShowCommand):
|
||||
@@ -158,6 +159,9 @@ class UpdateExtraDhcpOptMixin(object):
|
||||
default=[],
|
||||
action='append',
|
||||
dest='extra_dhcp_opts',
|
||||
type=utils.str2dict_type(
|
||||
required_keys=['opt_name'],
|
||||
optional_keys=['opt_value', 'ip_version']),
|
||||
help=_('Extra dhcp options to be assigned to this port: '
|
||||
'opt_name=<dhcp_option_name>,opt_value=<value>,'
|
||||
'ip_version={4,6}. You can repeat this option.'))
|
||||
@@ -174,7 +178,7 @@ class UpdateExtraDhcpOptMixin(object):
|
||||
"ip_version={4,6}. "
|
||||
"You can repeat this option.")
|
||||
for opt in parsed_args.extra_dhcp_opts:
|
||||
opt_ele.update(utils.str2dict(opt))
|
||||
opt_ele.update(opt)
|
||||
if ('opt_name' in opt_ele and
|
||||
('opt_value' in opt_ele or 'ip_version' in opt_ele)):
|
||||
if opt_ele.get('opt_value') == 'null':
|
||||
@@ -199,7 +203,9 @@ class UpdatePortAllowedAddressPair(object):
|
||||
default=[],
|
||||
action='append',
|
||||
dest='allowed_address_pairs',
|
||||
type=utils.str2dict,
|
||||
type=utils.str2dict_type(
|
||||
required_keys=['ip_address'],
|
||||
optional_keys=['mac_address']),
|
||||
help=_('Allowed address pair associated with the port.'
|
||||
'You can repeat this option.'))
|
||||
group_aap.add_argument(
|
||||
@@ -263,6 +269,7 @@ class CreatePort(neutronV20.CreateCommand, UpdatePortSecGroupMixin,
|
||||
parser.add_argument(
|
||||
'network_id', metavar='NETWORK',
|
||||
help=_('Network ID or name this port belongs to.'))
|
||||
dns.add_dns_argument_create(parser, self.resource, 'name')
|
||||
|
||||
def args2body(self, parsed_args):
|
||||
client = self.get_client()
|
||||
@@ -283,6 +290,7 @@ class CreatePort(neutronV20.CreateCommand, UpdatePortSecGroupMixin,
|
||||
self.args2body_extradhcpopt(parsed_args, body)
|
||||
self.args2body_qos_policy(parsed_args, body)
|
||||
self.args2body_allowedaddresspairs(parsed_args, body)
|
||||
dns.args2body_dns_create(parsed_args, body, 'name')
|
||||
|
||||
return {'port': body}
|
||||
|
||||
@@ -314,6 +322,7 @@ class UpdatePort(neutronV20.UpdateCommand, UpdatePortSecGroupMixin,
|
||||
self.add_arguments_extradhcpopt(parser)
|
||||
self.add_arguments_qos_policy(parser)
|
||||
self.add_arguments_allowedaddresspairs(parser)
|
||||
dns.add_dns_argument_update(parser, self.resource, 'name')
|
||||
|
||||
def args2body(self, parsed_args):
|
||||
body = {}
|
||||
@@ -326,5 +335,6 @@ class UpdatePort(neutronV20.UpdateCommand, UpdatePortSecGroupMixin,
|
||||
self.args2body_extradhcpopt(parsed_args, body)
|
||||
self.args2body_qos_policy(parsed_args, body)
|
||||
self.args2body_allowedaddresspairs(parsed_args, body)
|
||||
dns.args2body_dns_update(parsed_args, body, 'name')
|
||||
|
||||
return {'port': body}
|
||||
|
||||
147
neutronclient/neutron/v2_0/purge.py
Normal file
147
neutronclient/neutron/v2_0/purge.py
Normal file
@@ -0,0 +1,147 @@
|
||||
# Copyright 2016 Cisco Systems
|
||||
# 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 neutronclient._i18n import _
|
||||
from neutronclient.neutron import v2_0 as neutronV20
|
||||
|
||||
|
||||
class Purge(neutronV20.NeutronCommand):
|
||||
|
||||
def _pluralize(self, string):
|
||||
return string + 's'
|
||||
|
||||
def _get_resources(self, neutron_client, resource_types, tenant_id):
|
||||
resources = []
|
||||
for resource_type in resource_types:
|
||||
resources.append([])
|
||||
resource_type_plural = self._pluralize(resource_type)
|
||||
opts = {'fields': ['id', 'tenant_id']}
|
||||
if resource_type_plural == 'ports':
|
||||
opts['fields'].append('device_id')
|
||||
opts['fields'].append('device_owner')
|
||||
function = getattr(neutron_client, 'list_%s' %
|
||||
resource_type_plural)
|
||||
if callable(function):
|
||||
returned_resources = function(**opts).get(resource_type_plural,
|
||||
[])
|
||||
for resource in returned_resources:
|
||||
if resource['tenant_id'] == tenant_id:
|
||||
index = resource_types.index(resource_type)
|
||||
resources[index].append(resource)
|
||||
self.total_resources += 1
|
||||
return resources
|
||||
|
||||
def _delete_resource(self, neutron_client, resource_type, resource):
|
||||
resource_id = resource['id']
|
||||
if resource_type == 'port':
|
||||
if resource.get('device_owner', '') == 'network:router_interface':
|
||||
body = {'port_id': resource_id}
|
||||
neutron_client.remove_interface_router(resource['device_id'],
|
||||
body)
|
||||
return
|
||||
function = getattr(neutron_client, 'delete_%s' % resource_type)
|
||||
if callable(function):
|
||||
function(resource_id)
|
||||
|
||||
def _purge_resources(self, neutron_client, resource_types,
|
||||
tenant_resources):
|
||||
deleted = {}
|
||||
failed = {}
|
||||
failures = False
|
||||
for resources in tenant_resources:
|
||||
index = tenant_resources.index(resources)
|
||||
resource_type = resource_types[index]
|
||||
failed[resource_type] = 0
|
||||
deleted[resource_type] = 0
|
||||
for resource in resources:
|
||||
try:
|
||||
self._delete_resource(neutron_client, resource_type,
|
||||
resource)
|
||||
deleted[resource_type] += 1
|
||||
self.deleted_resources += 1
|
||||
except Exception:
|
||||
failures = True
|
||||
failed[resource_type] += 1
|
||||
self.total_resources -= 1
|
||||
percent_complete = 100
|
||||
if self.total_resources > 0:
|
||||
percent_complete = (self.deleted_resources /
|
||||
float(self.total_resources)) * 100
|
||||
sys.stdout.write("\rPurging resources: %d%% complete." %
|
||||
percent_complete)
|
||||
sys.stdout.flush()
|
||||
return (deleted, failed, failures)
|
||||
|
||||
def _build_message(self, deleted, failed, failures):
|
||||
msg = ''
|
||||
deleted_msg = []
|
||||
for resource, value in deleted.items():
|
||||
if value:
|
||||
if not msg:
|
||||
msg = 'Deleted'
|
||||
if not value == 1:
|
||||
resource = self._pluralize(resource)
|
||||
deleted_msg.append(" %d %s" % (value, resource))
|
||||
if deleted_msg:
|
||||
msg += ','.join(deleted_msg)
|
||||
|
||||
failed_msg = []
|
||||
if failures:
|
||||
if msg:
|
||||
msg += '. '
|
||||
msg += 'The following resources could not be deleted:'
|
||||
for resource, value in failed.items():
|
||||
if value:
|
||||
if not value == 1:
|
||||
resource = self._pluralize(resource)
|
||||
failed_msg.append(" %d %s" % (value, resource))
|
||||
msg += ','.join(failed_msg)
|
||||
|
||||
if msg:
|
||||
msg += '.'
|
||||
else:
|
||||
msg = _('Tenant has no supported resources.')
|
||||
|
||||
return msg
|
||||
|
||||
def get_parser(self, prog_name):
|
||||
parser = super(Purge, self).get_parser(prog_name)
|
||||
parser.add_argument(
|
||||
'tenant', metavar='TENANT',
|
||||
help=_('ID of Tenant owning the resources to be deleted.'))
|
||||
return parser
|
||||
|
||||
def take_action(self, parsed_args):
|
||||
neutron_client = self.get_client()
|
||||
|
||||
self.any_failures = False
|
||||
|
||||
# A list of the types of resources supported in the order in which
|
||||
# they should be deleted.
|
||||
resource_types = ['floatingip', 'port', 'router',
|
||||
'network', 'security_group']
|
||||
|
||||
deleted = {}
|
||||
failed = {}
|
||||
self.total_resources = 0
|
||||
self.deleted_resources = 0
|
||||
resources = self._get_resources(neutron_client, resource_types,
|
||||
parsed_args.tenant)
|
||||
deleted, failed, failures = self._purge_resources(neutron_client,
|
||||
resource_types,
|
||||
resources)
|
||||
print('\n%s' % self._build_message(deleted, failed, failures))
|
||||
@@ -37,7 +37,6 @@ def get_tenant_id(args, client):
|
||||
class DeleteQuota(neutronV20.NeutronCommand):
|
||||
"""Delete defined quotas of a given tenant."""
|
||||
|
||||
api = 'network'
|
||||
resource = 'quota'
|
||||
|
||||
def get_parser(self, prog_name):
|
||||
@@ -53,8 +52,7 @@ class DeleteQuota(neutronV20.NeutronCommand):
|
||||
help=argparse.SUPPRESS, nargs='?')
|
||||
return parser
|
||||
|
||||
def run(self, parsed_args):
|
||||
self.log.debug('run(%s)' % parsed_args)
|
||||
def take_action(self, parsed_args):
|
||||
neutron_client = self.get_client()
|
||||
tenant_id = get_tenant_id(parsed_args, neutron_client)
|
||||
obj_deleter = getattr(neutron_client,
|
||||
@@ -70,15 +68,13 @@ class DeleteQuota(neutronV20.NeutronCommand):
|
||||
class ListQuota(neutronV20.NeutronCommand, lister.Lister):
|
||||
"""List quotas of all tenants who have non-default quota values."""
|
||||
|
||||
api = 'network'
|
||||
resource = 'quota'
|
||||
|
||||
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)
|
||||
def take_action(self, parsed_args):
|
||||
neutron_client = self.get_client()
|
||||
search_opts = {}
|
||||
self.log.debug('search options: %s', search_opts)
|
||||
@@ -98,7 +94,6 @@ class ShowQuota(neutronV20.NeutronCommand, show.ShowOne):
|
||||
"""Show quotas of a given tenant.
|
||||
|
||||
"""
|
||||
api = 'network'
|
||||
resource = "quota"
|
||||
|
||||
def get_parser(self, prog_name):
|
||||
@@ -117,8 +112,7 @@ class ShowQuota(neutronV20.NeutronCommand, show.ShowOne):
|
||||
help=argparse.SUPPRESS, nargs='?')
|
||||
return parser
|
||||
|
||||
def get_data(self, parsed_args):
|
||||
self.log.debug('get_data(%s)', parsed_args)
|
||||
def take_action(self, parsed_args):
|
||||
neutron_client = self.get_client()
|
||||
tenant_id = get_tenant_id(parsed_args, neutron_client)
|
||||
params = {}
|
||||
@@ -216,8 +210,7 @@ class UpdateQuota(neutronV20.NeutronCommand, show.ShowOne):
|
||||
getattr(parsed_args, resource))
|
||||
return {self.resource: quota}
|
||||
|
||||
def get_data(self, parsed_args):
|
||||
self.log.debug('run(%s)', parsed_args)
|
||||
def take_action(self, parsed_args):
|
||||
neutron_client = self.get_client()
|
||||
_extra_values = neutronV20.parse_args_to_dict(self.values_specs)
|
||||
neutronV20._merge_args(self, parsed_args, _extra_values,
|
||||
|
||||
@@ -16,20 +16,31 @@
|
||||
from neutronclient._i18n import _
|
||||
from neutronclient.neutron import v2_0 as neutronV20
|
||||
|
||||
# key=object_type: value={key=resource, value=cmd_resource}
|
||||
RBAC_OBJECTS = {'network': {'network': 'network'},
|
||||
'qos-policy': {'policy': 'qos_policy'}}
|
||||
|
||||
def get_rbac_object_id(client, obj_type, obj_id_or_name):
|
||||
if obj_type == 'network':
|
||||
obj_id = neutronV20.find_resourceid_by_name_or_id(client,
|
||||
'network',
|
||||
obj_id_or_name)
|
||||
return obj_id
|
||||
|
||||
def _get_cmd_resource(obj_type):
|
||||
resource = list(RBAC_OBJECTS[obj_type])[0]
|
||||
cmd_resource = RBAC_OBJECTS[obj_type][resource]
|
||||
return resource, cmd_resource
|
||||
|
||||
|
||||
def get_rbac_obj_params(client, obj_type, obj_id_or_name):
|
||||
resource, cmd_resource = _get_cmd_resource(obj_type)
|
||||
obj_id = neutronV20.find_resourceid_by_name_or_id(
|
||||
client=client, resource=resource, name_or_id=obj_id_or_name,
|
||||
cmd_resource=cmd_resource)
|
||||
|
||||
return obj_id, cmd_resource
|
||||
|
||||
|
||||
class ListRBACPolicy(neutronV20.ListCommand):
|
||||
"""List RBAC policies that belong to a given tenant."""
|
||||
|
||||
resource = 'rbac_policy'
|
||||
list_columns = ['id', 'object_id']
|
||||
list_columns = ['id', 'object_type', 'object_id']
|
||||
pagination_support = True
|
||||
sorting_support = True
|
||||
allow_names = False
|
||||
@@ -53,7 +64,7 @@ class CreateRBACPolicy(neutronV20.CreateCommand):
|
||||
metavar='RBAC_OBJECT',
|
||||
help=_('ID or name of the RBAC object.'))
|
||||
parser.add_argument(
|
||||
'--type', choices=['network'],
|
||||
'--type', choices=RBAC_OBJECTS.keys(),
|
||||
required=True,
|
||||
help=_('Type of the object that RBAC policy affects.'))
|
||||
parser.add_argument(
|
||||
@@ -67,11 +78,13 @@ class CreateRBACPolicy(neutronV20.CreateCommand):
|
||||
|
||||
def args2body(self, parsed_args):
|
||||
neutron_client = self.get_client()
|
||||
_object_id = get_rbac_object_id(neutron_client, parsed_args.type,
|
||||
parsed_args.name)
|
||||
neutron_client.format = parsed_args.request_format
|
||||
_object_id, _object_type = get_rbac_obj_params(neutron_client,
|
||||
parsed_args.type,
|
||||
parsed_args.name)
|
||||
body = {
|
||||
'object_id': _object_id,
|
||||
'object_type': parsed_args.type,
|
||||
'object_type': _object_type,
|
||||
'target_tenant': parsed_args.target_tenant,
|
||||
'action': parsed_args.action,
|
||||
}
|
||||
|
||||
@@ -114,7 +114,8 @@ class UpdateRouter(neutronV20.UpdateCommand):
|
||||
routes_group = parser.add_mutually_exclusive_group()
|
||||
routes_group.add_argument(
|
||||
'--route', metavar='destination=CIDR,nexthop=IP_ADDR',
|
||||
action='append', dest='routes', type=utils.str2dict,
|
||||
action='append', dest='routes',
|
||||
type=utils.str2dict_type(required_keys=['destination', 'nexthop']),
|
||||
help=_('Route to associate with the router.'
|
||||
' You can repeat this option.'))
|
||||
routes_group.add_argument(
|
||||
@@ -138,7 +139,6 @@ class UpdateRouter(neutronV20.UpdateCommand):
|
||||
class RouterInterfaceCommand(neutronV20.NeutronCommand):
|
||||
"""Based class to Add/Remove router interface."""
|
||||
|
||||
api = 'network'
|
||||
resource = 'router'
|
||||
|
||||
def call_api(self, neutron_client, router_id, body):
|
||||
@@ -161,8 +161,7 @@ class RouterInterfaceCommand(neutronV20.NeutronCommand):
|
||||
'subnet.'))
|
||||
return parser
|
||||
|
||||
def run(self, parsed_args):
|
||||
self.log.debug('run(%s)' % parsed_args)
|
||||
def take_action(self, parsed_args):
|
||||
neutron_client = self.get_client()
|
||||
|
||||
if '=' in parsed_args.interface:
|
||||
@@ -211,7 +210,6 @@ class RemoveInterfaceRouter(RouterInterfaceCommand):
|
||||
class SetGatewayRouter(neutronV20.NeutronCommand):
|
||||
"""Set the external network gateway for a router."""
|
||||
|
||||
api = 'network'
|
||||
resource = 'router'
|
||||
|
||||
def get_parser(self, prog_name):
|
||||
@@ -226,14 +224,18 @@ class SetGatewayRouter(neutronV20.NeutronCommand):
|
||||
'--disable-snat', action='store_true',
|
||||
help=_('Disable source NAT on the router gateway.'))
|
||||
parser.add_argument(
|
||||
'--fixed-ip', action='append',
|
||||
'--fixed-ip', metavar='subnet_id=SUBNET,ip_address=IP_ADDR',
|
||||
action='append',
|
||||
type=utils.str2dict_type(optional_keys=['subnet_id',
|
||||
'ip_address']),
|
||||
help=_('Desired IP and/or subnet on external network: '
|
||||
'subnet_id=<name_or_id>,ip_address=<ip>. '
|
||||
'You can specify both of subnet_id and ip_address or '
|
||||
'specify one of them as well. '
|
||||
'You can repeat this option.'))
|
||||
return parser
|
||||
|
||||
def run(self, parsed_args):
|
||||
self.log.debug('run(%s)' % parsed_args)
|
||||
def take_action(self, parsed_args):
|
||||
neutron_client = self.get_client()
|
||||
_router_id = neutronV20.find_resourceid_by_name_or_id(
|
||||
neutron_client, self.resource, parsed_args.router)
|
||||
@@ -245,13 +247,12 @@ class SetGatewayRouter(neutronV20.NeutronCommand):
|
||||
if parsed_args.fixed_ip:
|
||||
ips = []
|
||||
for ip_spec in parsed_args.fixed_ip:
|
||||
ip_dict = utils.str2dict(ip_spec)
|
||||
subnet_name_id = ip_dict.get('subnet_id')
|
||||
subnet_name_id = ip_spec.get('subnet_id')
|
||||
if subnet_name_id:
|
||||
subnet_id = neutronV20.find_resourceid_by_name_or_id(
|
||||
neutron_client, 'subnet', subnet_name_id)
|
||||
ip_dict['subnet_id'] = subnet_id
|
||||
ips.append(ip_dict)
|
||||
ip_spec['subnet_id'] = subnet_id
|
||||
ips.append(ip_spec)
|
||||
router_dict['external_fixed_ips'] = ips
|
||||
neutron_client.add_gateway_router(_router_id, router_dict)
|
||||
print(_('Set gateway for router %s') % parsed_args.router,
|
||||
@@ -261,7 +262,6 @@ class SetGatewayRouter(neutronV20.NeutronCommand):
|
||||
class RemoveGatewayRouter(neutronV20.NeutronCommand):
|
||||
"""Remove an external network gateway from a router."""
|
||||
|
||||
api = 'network'
|
||||
resource = 'router'
|
||||
|
||||
def get_parser(self, prog_name):
|
||||
@@ -271,8 +271,7 @@ class RemoveGatewayRouter(neutronV20.NeutronCommand):
|
||||
help=_('ID or name of the router.'))
|
||||
return parser
|
||||
|
||||
def run(self, parsed_args):
|
||||
self.log.debug('run(%s)' % parsed_args)
|
||||
def take_action(self, parsed_args):
|
||||
neutron_client = self.get_client()
|
||||
_router_id = neutronV20.find_resourceid_by_name_or_id(
|
||||
neutron_client, self.resource, parsed_args.router)
|
||||
|
||||
@@ -62,16 +62,19 @@ def add_updatable_arguments(parser):
|
||||
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,
|
||||
action='append', dest='allocation_pools',
|
||||
type=utils.str2dict_type(required_keys=['start', 'end']),
|
||||
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,
|
||||
action='append', dest='allocation_pools',
|
||||
type=utils.str2dict_type(required_keys=['start', 'end']),
|
||||
help=argparse.SUPPRESS)
|
||||
parser.add_argument(
|
||||
'--host-route', metavar='destination=CIDR,nexthop=IP_ADDR',
|
||||
action='append', dest='host_routes', type=utils.str2dict,
|
||||
action='append', dest='host_routes',
|
||||
type=utils.str2dict_type(required_keys=['destination', 'nexthop']),
|
||||
help=_('Additional route (This option can be repeated).'))
|
||||
parser.add_argument(
|
||||
'--dns-nameserver', metavar='DNS_NAMESERVER',
|
||||
@@ -187,6 +190,10 @@ class CreateSubnet(neutronV20.CreateCommand):
|
||||
'--subnetpool', metavar='SUBNETPOOL',
|
||||
help=_('ID or name of subnetpool from which this subnet '
|
||||
'will obtain a CIDR.'))
|
||||
parser.add_argument(
|
||||
'--use-default-subnetpool',
|
||||
action='store_true',
|
||||
help=_('Use default subnetpool for ip_version, if it exists.'))
|
||||
parser.add_argument(
|
||||
'--prefixlen', metavar='PREFIX_LENGTH',
|
||||
help=_('Prefix length for subnet allocation from subnetpool.'))
|
||||
@@ -199,6 +206,8 @@ class CreateSubnet(neutronV20.CreateCommand):
|
||||
if parsed_args.prefixlen:
|
||||
body['prefixlen'] = parsed_args.prefixlen
|
||||
ip_version = parsed_args.ip_version
|
||||
if parsed_args.use_default_subnetpool:
|
||||
body['use_default_subnetpool'] = True
|
||||
if parsed_args.subnetpool:
|
||||
if parsed_args.subnetpool == 'None':
|
||||
_subnetpool_id = None
|
||||
|
||||
@@ -15,9 +15,17 @@
|
||||
#
|
||||
|
||||
from neutronclient._i18n import _
|
||||
from neutronclient.common import utils
|
||||
from neutronclient.neutron import v2_0 as neutronV20
|
||||
|
||||
|
||||
def _format_prefixes(subnetpool):
|
||||
try:
|
||||
return '\n'.join(pool for pool in subnetpool['prefixes'])
|
||||
except (TypeError, KeyError):
|
||||
return subnetpool['prefixes']
|
||||
|
||||
|
||||
def add_updatable_arguments(parser):
|
||||
parser.add_argument(
|
||||
'--min-prefixlen', type=int,
|
||||
@@ -32,20 +40,25 @@ def add_updatable_arguments(parser):
|
||||
'--pool-prefix',
|
||||
action='append', dest='prefixes',
|
||||
help=_('Subnetpool prefixes (This option can be repeated).'))
|
||||
utils.add_boolean_argument(
|
||||
parser, '--is-default',
|
||||
help=_('Specify whether this should be the default subnetpool '
|
||||
'(True meaning default).'))
|
||||
|
||||
|
||||
def updatable_args2body(parsed_args, body, for_create=True):
|
||||
neutronV20.update_dict(parsed_args, body,
|
||||
['name', 'prefixes', 'default_prefixlen',
|
||||
'min_prefixlen', 'max_prefixlen'])
|
||||
'min_prefixlen', 'max_prefixlen', 'is_default'])
|
||||
|
||||
|
||||
class ListSubnetPool(neutronV20.ListCommand):
|
||||
"""List subnetpools that belong to a given tenant."""
|
||||
|
||||
_formatters = {'prefixes': _format_prefixes, }
|
||||
resource = 'subnetpool'
|
||||
list_columns = ['id', 'name', 'prefixes',
|
||||
'default_prefixlen', 'address_scope_id']
|
||||
'default_prefixlen', 'address_scope_id', 'is_default']
|
||||
pagination_support = True
|
||||
sorting_support = True
|
||||
|
||||
@@ -69,6 +82,7 @@ class CreateSubnetPool(neutronV20.CreateCommand):
|
||||
help=_('Set the subnetpool as shared.'))
|
||||
parser.add_argument(
|
||||
'name',
|
||||
metavar='NAME',
|
||||
help=_('Name of subnetpool to create.'))
|
||||
parser.add_argument(
|
||||
'--address-scope',
|
||||
|
||||
105
neutronclient/neutron/v2_0/tag.py
Normal file
105
neutronclient/neutron/v2_0/tag.py
Normal file
@@ -0,0 +1,105 @@
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
from neutronclient._i18n import _
|
||||
from neutronclient.common import exceptions
|
||||
from neutronclient.neutron import v2_0 as neutronv20
|
||||
|
||||
|
||||
# List of resources can be set tag
|
||||
TAG_RESOURCES = ['network']
|
||||
|
||||
|
||||
def _convert_resource_args(client, parsed_args):
|
||||
resource_type = neutronv20._get_resource_plural(
|
||||
parsed_args.resource_type, client)
|
||||
resource_id = neutronv20.find_resourceid_by_name_or_id(
|
||||
client, parsed_args.resource_type, parsed_args.resource)
|
||||
return resource_type, resource_id
|
||||
|
||||
|
||||
def _add_common_arguments(parser):
|
||||
parser.add_argument('--resource-type',
|
||||
choices=TAG_RESOURCES,
|
||||
dest='resource_type',
|
||||
required=True,
|
||||
help=_('Resource Type.'))
|
||||
parser.add_argument('--resource',
|
||||
required=True,
|
||||
help=_('Resource name or ID.'))
|
||||
|
||||
|
||||
class AddTag(neutronv20.NeutronCommand):
|
||||
"""Add a tag into the resource."""
|
||||
|
||||
def get_parser(self, prog_name):
|
||||
parser = super(AddTag, self).get_parser(prog_name)
|
||||
_add_common_arguments(parser)
|
||||
parser.add_argument('--tag',
|
||||
required=True,
|
||||
help=_('Tag to be added.'))
|
||||
return parser
|
||||
|
||||
def take_action(self, parsed_args):
|
||||
client = self.get_client()
|
||||
resource_type, resource_id = _convert_resource_args(client,
|
||||
parsed_args)
|
||||
client.add_tag(resource_type, resource_id, parsed_args.tag)
|
||||
|
||||
|
||||
class ReplaceTag(neutronv20.NeutronCommand):
|
||||
"""Replace all tags on the resource."""
|
||||
|
||||
def get_parser(self, prog_name):
|
||||
parser = super(ReplaceTag, self).get_parser(prog_name)
|
||||
_add_common_arguments(parser)
|
||||
parser.add_argument('--tag',
|
||||
metavar='TAG',
|
||||
action='append',
|
||||
dest='tags',
|
||||
required=True,
|
||||
help=_('Tag (This option can be repeated).'))
|
||||
return parser
|
||||
|
||||
def take_action(self, parsed_args):
|
||||
client = self.get_client()
|
||||
resource_type, resource_id = _convert_resource_args(client,
|
||||
parsed_args)
|
||||
body = {'tags': parsed_args.tags}
|
||||
client.replace_tag(resource_type, resource_id, body)
|
||||
|
||||
|
||||
class RemoveTag(neutronv20.NeutronCommand):
|
||||
"""Remove a tag on the resource."""
|
||||
|
||||
def get_parser(self, prog_name):
|
||||
parser = super(RemoveTag, self).get_parser(prog_name)
|
||||
_add_common_arguments(parser)
|
||||
tag_opt = parser.add_mutually_exclusive_group()
|
||||
tag_opt.add_argument('--all',
|
||||
action='store_true',
|
||||
help=_('Remove all tags on the resource.'))
|
||||
tag_opt.add_argument('--tag',
|
||||
help=_('Tag to be removed.'))
|
||||
return parser
|
||||
|
||||
def take_action(self, parsed_args):
|
||||
if not parsed_args.all and not parsed_args.tag:
|
||||
raise exceptions.CommandError(
|
||||
_("--all or --tag must be specified"))
|
||||
client = self.get_client()
|
||||
resource_type, resource_id = _convert_resource_args(client,
|
||||
parsed_args)
|
||||
if parsed_args.all:
|
||||
client.remove_tag_all(resource_type, resource_id)
|
||||
else:
|
||||
client.remove_tag(resource_type, resource_id, parsed_args.tag)
|
||||
@@ -35,6 +35,7 @@ class ShowIKEPolicy(neutronv20.ShowCommand):
|
||||
"""Show information of a given IKE policy."""
|
||||
|
||||
resource = 'ikepolicy'
|
||||
help_resource = 'IKE policy'
|
||||
|
||||
|
||||
class CreateIKEPolicy(neutronv20.CreateCommand):
|
||||
@@ -70,7 +71,7 @@ class CreateIKEPolicy(neutronv20.CreateCommand):
|
||||
parser.add_argument(
|
||||
'--lifetime',
|
||||
metavar="units=UNITS,value=VALUE",
|
||||
type=utils.str2dict,
|
||||
type=utils.str2dict_type(optional_keys=['units', 'value']),
|
||||
help=vpn_utils.lifetime_help("IKE"))
|
||||
parser.add_argument(
|
||||
'name', metavar='NAME',
|
||||
@@ -93,12 +94,13 @@ class UpdateIKEPolicy(neutronv20.UpdateCommand):
|
||||
"""Update a given IKE policy."""
|
||||
|
||||
resource = 'ikepolicy'
|
||||
help_resource = 'IKE policy'
|
||||
|
||||
def add_known_arguments(self, parser):
|
||||
parser.add_argument(
|
||||
'--lifetime',
|
||||
metavar="units=UNITS,value=VALUE",
|
||||
type=utils.str2dict,
|
||||
type=utils.str2dict_type(optional_keys=['units', 'value']),
|
||||
help=vpn_utils.lifetime_help("IKE"))
|
||||
|
||||
def args2body(self, parsed_args):
|
||||
@@ -114,3 +116,4 @@ class DeleteIKEPolicy(neutronv20.DeleteCommand):
|
||||
"""Delete a given IKE policy."""
|
||||
|
||||
resource = 'ikepolicy'
|
||||
help_resource = 'IKE policy'
|
||||
|
||||
@@ -46,6 +46,7 @@ class ShowIPsecSiteConnection(neutronv20.ShowCommand):
|
||||
"""Show information of a given IPsec site connection."""
|
||||
|
||||
resource = 'ipsec_site_connection'
|
||||
help_resource = 'IPsec site connection'
|
||||
|
||||
|
||||
class IPsecSiteConnectionMixin(object):
|
||||
@@ -54,7 +55,8 @@ class IPsecSiteConnectionMixin(object):
|
||||
parser.add_argument(
|
||||
'--dpd',
|
||||
metavar="action=ACTION,interval=INTERVAL,timeout=TIMEOUT",
|
||||
type=utils.str2dict,
|
||||
type=utils.str2dict_type(
|
||||
optional_keys=['action', 'interval', 'timeout']),
|
||||
help=vpn_utils.dpd_help("IPsec connection."))
|
||||
parser.add_argument(
|
||||
'--local-ep-group',
|
||||
@@ -196,9 +198,11 @@ class UpdateIPsecSiteConnection(IPsecSiteConnectionMixin,
|
||||
"""Update a given IPsec site connection."""
|
||||
|
||||
resource = 'ipsec_site_connection'
|
||||
help_resource = 'IPsec site connection'
|
||||
|
||||
|
||||
class DeleteIPsecSiteConnection(neutronv20.DeleteCommand):
|
||||
"""Delete a given IPsec site connection."""
|
||||
|
||||
resource = 'ipsec_site_connection'
|
||||
help_resource = 'IPsec site connection'
|
||||
|
||||
@@ -35,6 +35,7 @@ class ShowIPsecPolicy(neutronv20.ShowCommand):
|
||||
"""Show information of a given IPsec policy."""
|
||||
|
||||
resource = 'ipsecpolicy'
|
||||
help_resource = 'IPsec policy'
|
||||
|
||||
|
||||
class CreateIPsecPolicy(neutronv20.CreateCommand):
|
||||
@@ -69,7 +70,7 @@ class CreateIPsecPolicy(neutronv20.CreateCommand):
|
||||
parser.add_argument(
|
||||
'--lifetime',
|
||||
metavar="units=UNITS,value=VALUE",
|
||||
type=utils.str2dict,
|
||||
type=utils.str2dict_type(optional_keys=['units', 'value']),
|
||||
help=vpn_utils.lifetime_help("IPsec"))
|
||||
parser.add_argument(
|
||||
'name', metavar='NAME',
|
||||
@@ -92,12 +93,13 @@ class UpdateIPsecPolicy(neutronv20.UpdateCommand):
|
||||
"""Update a given IPsec policy."""
|
||||
|
||||
resource = 'ipsecpolicy'
|
||||
help_resource = 'IPsec policy'
|
||||
|
||||
def add_known_arguments(self, parser):
|
||||
parser.add_argument(
|
||||
'--lifetime',
|
||||
metavar="units=UNITS,value=VALUE",
|
||||
type=utils.str2dict,
|
||||
type=utils.str2dict_type(optional_keys=['units', 'value']),
|
||||
help=vpn_utils.lifetime_help("IPsec"))
|
||||
|
||||
def args2body(self, parsed_args):
|
||||
@@ -113,3 +115,4 @@ class DeleteIPsecPolicy(neutronv20.DeleteCommand):
|
||||
"""Delete a given IPsec policy."""
|
||||
|
||||
resource = 'ipsecpolicy'
|
||||
help_resource = 'IPsec policy'
|
||||
|
||||
@@ -34,6 +34,7 @@ class ShowVPNService(neutronv20.ShowCommand):
|
||||
"""Show information of a given VPN service."""
|
||||
|
||||
resource = 'vpnservice'
|
||||
help_resource = 'VPN service'
|
||||
|
||||
|
||||
class CreateVPNService(neutronv20.CreateCommand):
|
||||
@@ -83,9 +84,11 @@ class UpdateVPNService(neutronv20.UpdateCommand):
|
||||
"""Update a given VPN service."""
|
||||
|
||||
resource = 'vpnservice'
|
||||
help_resource = 'VPN service'
|
||||
|
||||
|
||||
class DeleteVPNService(neutronv20.DeleteCommand):
|
||||
"""Delete a given VPN service."""
|
||||
|
||||
resource = 'vpnservice'
|
||||
help_resource = 'VPN service'
|
||||
|
||||
@@ -32,18 +32,22 @@ import os_client_config
|
||||
from oslo_utils import encodeutils
|
||||
|
||||
from cliff import app
|
||||
from cliff import command
|
||||
from cliff import commandmanager
|
||||
|
||||
from neutronclient._i18n import _
|
||||
from neutronclient.common import clientmanager
|
||||
from neutronclient.common import command as openstack_command
|
||||
from neutronclient.common import exceptions as exc
|
||||
from neutronclient.common import extension as client_extension
|
||||
from neutronclient.common import utils
|
||||
from neutronclient.neutron.v2_0 import address_scope
|
||||
from neutronclient.neutron.v2_0 import agent
|
||||
from neutronclient.neutron.v2_0 import agentscheduler
|
||||
from neutronclient.neutron.v2_0 import auto_allocated_topology
|
||||
from neutronclient.neutron.v2_0 import availability_zone
|
||||
from neutronclient.neutron.v2_0.bgp import dragentscheduler as bgp_drsched
|
||||
from neutronclient.neutron.v2_0.bgp import peer as bgp_peer
|
||||
from neutronclient.neutron.v2_0.bgp import speaker as bgp_speaker
|
||||
from neutronclient.neutron.v2_0 import extension
|
||||
from neutronclient.neutron.v2_0.flavor import flavor
|
||||
from neutronclient.neutron.v2_0.flavor import flavor_profile
|
||||
@@ -55,6 +59,8 @@ from neutronclient.neutron.v2_0.lb import healthmonitor as lb_healthmonitor
|
||||
from neutronclient.neutron.v2_0.lb import member as lb_member
|
||||
from neutronclient.neutron.v2_0.lb import pool as lb_pool
|
||||
from neutronclient.neutron.v2_0.lb.v2 import healthmonitor as lbaas_healthmon
|
||||
from neutronclient.neutron.v2_0.lb.v2 import l7policy as lbaas_l7policy
|
||||
from neutronclient.neutron.v2_0.lb.v2 import l7rule as lbaas_l7rule
|
||||
from neutronclient.neutron.v2_0.lb.v2 import listener as lbaas_listener
|
||||
from neutronclient.neutron.v2_0.lb.v2 import loadbalancer as lbaas_loadbalancer
|
||||
from neutronclient.neutron.v2_0.lb.v2 import member as lbaas_member
|
||||
@@ -62,9 +68,11 @@ from neutronclient.neutron.v2_0.lb.v2 import pool as lbaas_pool
|
||||
from neutronclient.neutron.v2_0.lb import vip as lb_vip
|
||||
from neutronclient.neutron.v2_0 import metering
|
||||
from neutronclient.neutron.v2_0 import network
|
||||
from neutronclient.neutron.v2_0 import network_ip_availability
|
||||
from neutronclient.neutron.v2_0.nsx import networkgateway
|
||||
from neutronclient.neutron.v2_0.nsx import qos_queue
|
||||
from neutronclient.neutron.v2_0 import port
|
||||
from neutronclient.neutron.v2_0 import purge
|
||||
from neutronclient.neutron.v2_0.qos import bandwidth_limit_rule
|
||||
from neutronclient.neutron.v2_0.qos import policy as qos_policy
|
||||
from neutronclient.neutron.v2_0.qos import rule as qos_rule
|
||||
@@ -75,6 +83,7 @@ from neutronclient.neutron.v2_0 import securitygroup
|
||||
from neutronclient.neutron.v2_0 import servicetype
|
||||
from neutronclient.neutron.v2_0 import subnet
|
||||
from neutronclient.neutron.v2_0 import subnetpool
|
||||
from neutronclient.neutron.v2_0 import tag
|
||||
from neutronclient.neutron.v2_0.vpn import endpoint_group
|
||||
from neutronclient.neutron.v2_0.vpn import ikepolicy
|
||||
from neutronclient.neutron.v2_0.vpn import ipsec_site_connection
|
||||
@@ -140,9 +149,12 @@ def check_non_negative_int(value):
|
||||
return value
|
||||
|
||||
|
||||
class BashCompletionCommand(openstack_command.OpenStackCommand):
|
||||
class BashCompletionCommand(command.Command):
|
||||
"""Prints all of the commands and options for bash-completion."""
|
||||
resource = "bash_completion"
|
||||
|
||||
def take_action(self, parsed_args):
|
||||
pass
|
||||
|
||||
|
||||
COMMAND_V2 = {
|
||||
'bash-completion': BashCompletionCommand,
|
||||
@@ -167,6 +179,7 @@ COMMAND_V2 = {
|
||||
'port-create': port.CreatePort,
|
||||
'port-delete': port.DeletePort,
|
||||
'port-update': port.UpdatePort,
|
||||
'purge': purge.Purge,
|
||||
'quota-list': quota.ListQuota,
|
||||
'quota-show': quota.ShowQuota,
|
||||
'quota-delete': quota.DeleteQuota,
|
||||
@@ -204,11 +217,22 @@ COMMAND_V2 = {
|
||||
'lbaas-loadbalancer-update': lbaas_loadbalancer.UpdateLoadBalancer,
|
||||
'lbaas-loadbalancer-delete': lbaas_loadbalancer.DeleteLoadBalancer,
|
||||
'lbaas-loadbalancer-stats': lbaas_loadbalancer.RetrieveLoadBalancerStats,
|
||||
'lbaas-loadbalancer-status': lbaas_loadbalancer.RetrieveLoadBalancerStatus,
|
||||
'lbaas-listener-list': lbaas_listener.ListListener,
|
||||
'lbaas-listener-show': lbaas_listener.ShowListener,
|
||||
'lbaas-listener-create': lbaas_listener.CreateListener,
|
||||
'lbaas-listener-update': lbaas_listener.UpdateListener,
|
||||
'lbaas-listener-delete': lbaas_listener.DeleteListener,
|
||||
'lbaas-l7policy-list': lbaas_l7policy.ListL7Policy,
|
||||
'lbaas-l7policy-show': lbaas_l7policy.ShowL7Policy,
|
||||
'lbaas-l7policy-create': lbaas_l7policy.CreateL7Policy,
|
||||
'lbaas-l7policy-update': lbaas_l7policy.UpdateL7Policy,
|
||||
'lbaas-l7policy-delete': lbaas_l7policy.DeleteL7Policy,
|
||||
'lbaas-l7rule-list': lbaas_l7rule.ListL7Rule,
|
||||
'lbaas-l7rule-show': lbaas_l7rule.ShowL7Rule,
|
||||
'lbaas-l7rule-create': lbaas_l7rule.CreateL7Rule,
|
||||
'lbaas-l7rule-update': lbaas_l7rule.UpdateL7Rule,
|
||||
'lbaas-l7rule-delete': lbaas_l7rule.DeleteL7Rule,
|
||||
'lbaas-pool-list': lbaas_pool.ListPool,
|
||||
'lbaas-pool-show': lbaas_pool.ShowPool,
|
||||
'lbaas-pool-create': lbaas_pool.CreatePool,
|
||||
@@ -388,6 +412,42 @@ COMMAND_V2 = {
|
||||
'flavor-profile-delete': flavor_profile.DeleteFlavorProfile,
|
||||
'flavor-profile-update': flavor_profile.UpdateFlavorProfile,
|
||||
'availability-zone-list': availability_zone.ListAvailabilityZone,
|
||||
'auto-allocated-topology-show': (
|
||||
auto_allocated_topology.ShowAutoAllocatedTopology),
|
||||
'bgp-dragent-speaker-add': (
|
||||
bgp_drsched.AddBGPSpeakerToDRAgent
|
||||
),
|
||||
'bgp-dragent-speaker-remove': (
|
||||
bgp_drsched.RemoveBGPSpeakerFromDRAgent
|
||||
),
|
||||
'bgp-speaker-list-on-dragent': (
|
||||
bgp_drsched.ListBGPSpeakersOnDRAgent
|
||||
),
|
||||
'bgp-dragent-list-hosting-speaker': (
|
||||
bgp_drsched.ListDRAgentsHostingBGPSpeaker
|
||||
),
|
||||
'bgp-speaker-list': bgp_speaker.ListSpeakers,
|
||||
'bgp-speaker-advertiseroute-list': (
|
||||
bgp_speaker.ListRoutesAdvertisedBySpeaker
|
||||
),
|
||||
'bgp-speaker-show': bgp_speaker.ShowSpeaker,
|
||||
'bgp-speaker-create': bgp_speaker.CreateSpeaker,
|
||||
'bgp-speaker-update': bgp_speaker.UpdateSpeaker,
|
||||
'bgp-speaker-delete': bgp_speaker.DeleteSpeaker,
|
||||
'bgp-speaker-peer-add': bgp_speaker.AddPeerToSpeaker,
|
||||
'bgp-speaker-peer-remove': bgp_speaker.RemovePeerFromSpeaker,
|
||||
'bgp-speaker-network-add': bgp_speaker.AddNetworkToSpeaker,
|
||||
'bgp-speaker-network-remove': bgp_speaker.RemoveNetworkFromSpeaker,
|
||||
'bgp-peer-list': bgp_peer.ListPeers,
|
||||
'bgp-peer-show': bgp_peer.ShowPeer,
|
||||
'bgp-peer-create': bgp_peer.CreatePeer,
|
||||
'bgp-peer-update': bgp_peer.UpdatePeer,
|
||||
'bgp-peer-delete': bgp_peer.DeletePeer,
|
||||
'net-ip-availability-list': network_ip_availability.ListIpAvailability,
|
||||
'net-ip-availability-show': network_ip_availability.ShowIpAvailability,
|
||||
'tag-add': tag.AddTag,
|
||||
'tag-replace': tag.ReplaceTag,
|
||||
'tag-remove': tag.RemoveTag,
|
||||
}
|
||||
|
||||
COMMANDS = {'2.0': COMMAND_V2}
|
||||
@@ -727,9 +787,9 @@ class NeutronShell(app.App):
|
||||
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()
|
||||
for _name, _command in self.command_manager:
|
||||
commands.add(_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():
|
||||
@@ -838,11 +898,22 @@ class NeutronShell(app.App):
|
||||
cloud=self.options.os_cloud, argparse=self.options,
|
||||
network_api_version=self.api_version)
|
||||
verify, cert = cloud_config.get_requests_verify_args()
|
||||
auth = cloud_config.get_auth()
|
||||
|
||||
auth_session = session.Session(
|
||||
auth=auth, verify=verify, cert=cert,
|
||||
timeout=self.options.http_timeout)
|
||||
# TODO(singhj): Remove dependancy on HTTPClient
|
||||
# for the case of token-endpoint authentication
|
||||
|
||||
# When using token-endpoint authentication legacy
|
||||
# HTTPClient will be used, otherwise SessionClient
|
||||
# will be used.
|
||||
if self.options.os_token and self.options.os_url:
|
||||
auth = None
|
||||
auth_session = None
|
||||
else:
|
||||
auth = cloud_config.get_auth()
|
||||
|
||||
auth_session = session.Session(
|
||||
auth=auth, verify=verify, cert=cert,
|
||||
timeout=self.options.http_timeout)
|
||||
|
||||
interface = self.options.os_endpoint_type or self.endpoint_type
|
||||
if interface.endswith('URL'):
|
||||
@@ -851,6 +922,8 @@ class NeutronShell(app.App):
|
||||
retries=self.options.retries,
|
||||
raise_errors=False,
|
||||
session=auth_session,
|
||||
url=self.options.os_url,
|
||||
token=self.options.os_token,
|
||||
region_name=cloud_config.get_region_name(),
|
||||
api_version=cloud_config.get_api_version('network'),
|
||||
service_type=cloud_config.get_service_type('network'),
|
||||
|
||||
172
neutronclient/tests/functional/core/test_purge.py
Normal file
172
neutronclient/tests/functional/core/test_purge.py
Normal file
@@ -0,0 +1,172 @@
|
||||
# Copyright 2016 Cisco Systems
|
||||
# All Rights Reserved
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
from neutronclient.tests.functional import base
|
||||
|
||||
from tempest_lib import exceptions
|
||||
|
||||
|
||||
class PurgeNeutronClientCLITest(base.ClientTestBase):
|
||||
|
||||
def _safe_cleanup(self, delete_command):
|
||||
try:
|
||||
self.neutron(delete_command)
|
||||
except exceptions.CommandFailed:
|
||||
# This resource was already purged successfully
|
||||
pass
|
||||
|
||||
def _create_subnet(self, name, tenant_id, cidr):
|
||||
params = ('%(name)s --name %(name)s --tenant-id %(tenant)s '
|
||||
'%(cidr)s' % {'name': name,
|
||||
'tenant': tenant_id,
|
||||
'cidr': cidr})
|
||||
subnet = self.parser.listing(self.neutron('subnet-create',
|
||||
params=params))
|
||||
for row in subnet:
|
||||
if row['Field'] == 'id':
|
||||
return row['Value']
|
||||
|
||||
def _create_router(self, name, tenant_id):
|
||||
params = ('%(name)s --tenant_id %(tenant)s' % {'name': name,
|
||||
'tenant': tenant_id})
|
||||
router = self.parser.listing(self.neutron('router-create',
|
||||
params=params))
|
||||
for row in router:
|
||||
if row['Field'] == 'id':
|
||||
return row['Value']
|
||||
|
||||
def _create_floatingip(self, network, tenant_id):
|
||||
params = ('%(network)s --tenant-id %(tenant)s' %
|
||||
{'network': network, 'tenant': tenant_id})
|
||||
floatingip = self.parser.listing(self.neutron('floatingip-create',
|
||||
params=params))
|
||||
for row in floatingip:
|
||||
if row['Field'] == 'id':
|
||||
return row['Value']
|
||||
|
||||
def _create_resources(self, name, tenant_id, shared_tenant_id=None):
|
||||
# If no shared_tenant_id is provided, create the resources for the
|
||||
# current tenant to test that they will be deleted when not in use.
|
||||
if not shared_tenant_id:
|
||||
shared_tenant_id = tenant_id
|
||||
|
||||
self.neutron('net-create',
|
||||
params=('%(name)s --router:external True '
|
||||
'--tenant-id %(tenant)s' % {'name': name,
|
||||
'tenant': tenant_id}))
|
||||
self.addCleanup(self._safe_cleanup, 'net-delete %s' % name)
|
||||
|
||||
self.neutron('net-create',
|
||||
params=('%(name)s-shared --shared '
|
||||
'--tenant-id %(tenant)s' %
|
||||
{'name': name, 'tenant': shared_tenant_id}))
|
||||
self.addCleanup(self._safe_cleanup,
|
||||
'net-delete %s-shared' % name)
|
||||
|
||||
subnet = self._create_subnet(name, tenant_id, '192.168.71.0/24')
|
||||
self.addCleanup(self._safe_cleanup, 'subnet-delete %s' % name)
|
||||
|
||||
subnet = self._create_subnet('%s-shared' % name, tenant_id,
|
||||
'192.168.81.0/24')
|
||||
self.addCleanup(self._safe_cleanup, 'subnet-delete %s-shared' % name)
|
||||
|
||||
router = self._create_router(name, tenant_id)
|
||||
self.addCleanup(self._safe_cleanup, 'router-delete %s' % name)
|
||||
|
||||
self.neutron('router-interface-add',
|
||||
params=('%(router)s %(subnet)s '
|
||||
'--tenant-id %(tenant)s' % {'router': router,
|
||||
'subnet': subnet,
|
||||
'tenant': tenant_id}))
|
||||
|
||||
self.neutron('port-create',
|
||||
params=('%(name)s --name %(name)s '
|
||||
'--tenant-id %(tenant)s' % {'name': name,
|
||||
'tenant': tenant_id}))
|
||||
self.addCleanup(self._safe_cleanup, 'port-delete %s' % name)
|
||||
|
||||
self.neutron('port-create',
|
||||
params=('%(name)s-shared --name %(name)s-shared '
|
||||
'--tenant-id %(tenant)s' % {'name': name,
|
||||
'tenant': tenant_id}))
|
||||
self.addCleanup(self._safe_cleanup, 'port-delete %s-shared' % name)
|
||||
|
||||
self.neutron('security-group-create',
|
||||
params=('%(name)s --tenant-id %(tenant)s' %
|
||||
{'name': name, 'tenant': tenant_id}))
|
||||
self.addCleanup(self._safe_cleanup, 'security-group-delete %s' % name)
|
||||
|
||||
floatingip = self._create_floatingip(name, tenant_id)
|
||||
self.addCleanup(self._safe_cleanup, ('floatingip-delete '
|
||||
'%s' % floatingip))
|
||||
return floatingip
|
||||
|
||||
def _verify_deletion(self, resources, resource_type):
|
||||
purged = True
|
||||
no_purge_purged = True
|
||||
for row in resources:
|
||||
if resource_type == 'port' and row.get('id', None):
|
||||
port = self.parser.listing(self.neutron('port-show',
|
||||
params=row['id']))
|
||||
port_dict = {}
|
||||
for row in port:
|
||||
port_dict[row['Field']] = row['Value']
|
||||
if port_dict['device_owner'] == 'network:router_interface':
|
||||
if port_dict['tenant_id'] == 'purge-tenant':
|
||||
purged = False
|
||||
elif port_dict['tenant_id'] == 'no-purge-tenant':
|
||||
no_purge_purged = False
|
||||
if not purged or not no_purge_purged:
|
||||
self.addCleanup(self.neutron,
|
||||
('router-interface-delete %(router)s '
|
||||
'port=%(port)s' %
|
||||
{'router': port_dict['device_id'],
|
||||
'port': port_dict['id']}))
|
||||
if (row.get('name') == 'purge-me' or
|
||||
row.get('id') == self.purge_floatingip):
|
||||
purged = False
|
||||
elif ('no-purge' in row.get('name', '') or
|
||||
row.get('id') == self.no_purge_floatingip):
|
||||
no_purge_purged = False
|
||||
|
||||
if not purged:
|
||||
self.fail('%s not deleted by neutron purge' % resource_type)
|
||||
|
||||
if no_purge_purged:
|
||||
self.fail('%s owned by another tenant incorrectly deleted '
|
||||
'by neutron purge' % resource_type)
|
||||
|
||||
def test_purge(self):
|
||||
self.purge_floatingip = self._create_resources('purge-me',
|
||||
'purge-tenant')
|
||||
self.no_purge_floatingip = self._create_resources('no-purge',
|
||||
'no-purge-tenant',
|
||||
'purge-tenant')
|
||||
|
||||
purge_output = self.neutron('purge', params='purge-tenant').strip()
|
||||
if not purge_output:
|
||||
self.fail('Purge command did not return feedback')
|
||||
|
||||
networks = self.parser.listing(self.neutron('net-list'))
|
||||
subnets = self.parser.listing(self.neutron('subnet-list'))
|
||||
routers = self.parser.listing(self.neutron('router-list'))
|
||||
ports = self.parser.listing(self.neutron('port-list'))
|
||||
floatingips = self.parser.listing(self.neutron('floatingip-list'))
|
||||
|
||||
self._verify_deletion(networks, 'network')
|
||||
self._verify_deletion(subnets, 'subnet')
|
||||
self._verify_deletion(ports, 'port')
|
||||
self._verify_deletion(routers, 'router')
|
||||
self._verify_deletion(floatingips, 'floatingip')
|
||||
0
neutronclient/tests/unit/bgp/__init__.py
Normal file
0
neutronclient/tests/unit/bgp/__init__.py
Normal file
66
neutronclient/tests/unit/bgp/test_cli20_dragentscheduler.py
Normal file
66
neutronclient/tests/unit/bgp/test_cli20_dragentscheduler.py
Normal file
@@ -0,0 +1,66 @@
|
||||
# Copyright 2016 Huawei Technologies India Pvt. Ltd.
|
||||
# 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 neutronclient.neutron.v2_0.bgp import dragentscheduler as bgp_drsched
|
||||
from neutronclient.tests.unit import test_cli20
|
||||
from neutronclient.tests.unit import test_cli20_agentschedulers as test_as
|
||||
|
||||
|
||||
BGP_DRAGENT_ID = 'bgp_dragent_id1'
|
||||
BGP_SPEAKER = 'bgp_speaker_id1'
|
||||
|
||||
|
||||
class CLITestV20DRAgentScheduler(test_as.CLITestV20AgentScheduler):
|
||||
|
||||
def test_add_bgp_speaker_to_dragent(self):
|
||||
resource = 'agent'
|
||||
cmd = bgp_drsched.AddBGPSpeakerToDRAgent(
|
||||
test_cli20.MyApp(sys.stdout), None)
|
||||
args = (BGP_DRAGENT_ID, BGP_SPEAKER)
|
||||
body = {'bgp_speaker_id': BGP_SPEAKER}
|
||||
result = {'bgp_speaker_id': 'bgp_speaker_id', }
|
||||
self._test_add_to_agent(resource, cmd, args,
|
||||
self.client.BGP_DRINSTANCES,
|
||||
body, result)
|
||||
|
||||
def test_remove_bgp_speaker_from_dragent(self):
|
||||
resource = 'agent'
|
||||
cmd = bgp_drsched.RemoveBGPSpeakerFromDRAgent(
|
||||
test_cli20.MyApp(sys.stdout), None)
|
||||
args = (BGP_DRAGENT_ID, BGP_SPEAKER)
|
||||
self._test_remove_from_agent(resource, cmd, args,
|
||||
self.client.BGP_DRINSTANCES)
|
||||
|
||||
def test_list_bgp_speakers_on_dragent(self):
|
||||
resources = 'bgp_speakers'
|
||||
cmd = bgp_drsched.ListBGPSpeakersOnDRAgent(
|
||||
test_cli20.MyApp(sys.stdout), None)
|
||||
path = ((self.client.agent_path + self.client.BGP_DRINSTANCES) %
|
||||
BGP_DRAGENT_ID)
|
||||
self._test_list_resources(resources, cmd, base_args=[BGP_DRAGENT_ID],
|
||||
path=path)
|
||||
|
||||
def test_list_dragents_hosting_bgp_speaker(self):
|
||||
resources = 'agent'
|
||||
cmd = bgp_drsched.ListDRAgentsHostingBGPSpeaker(
|
||||
test_cli20.MyApp(sys.stdout), None)
|
||||
path = ((self.client.bgp_speaker_path + self.client.BGP_DRAGENTS) %
|
||||
BGP_DRAGENT_ID)
|
||||
contents = {self.id_field: 'myid1', 'alive': True}
|
||||
self._test_list_resources(resources, cmd, base_args=[BGP_DRAGENT_ID],
|
||||
path=path, response_contents=contents)
|
||||
223
neutronclient/tests/unit/bgp/test_cli20_peer.py
Normal file
223
neutronclient/tests/unit/bgp/test_cli20_peer.py
Normal file
@@ -0,0 +1,223 @@
|
||||
# Copyright 2016 Huawei Technologies India Pvt. Ltd.
|
||||
# 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 neutronclient.common import exceptions
|
||||
from neutronclient.neutron.v2_0.bgp import peer as bgp_peer
|
||||
from neutronclient.neutron.v2_0.bgp import speaker as bgp_speaker
|
||||
from neutronclient.tests.unit import test_cli20
|
||||
|
||||
|
||||
class CLITestV20BGPPeerJSON(test_cli20.CLITestV20Base):
|
||||
|
||||
non_admin_status_resources = ['bgp_peer']
|
||||
|
||||
def test_create_bgp_peer_with_mandatory_params(self):
|
||||
# Create BGP peer with mandatory params.
|
||||
resource = 'bgp_peer'
|
||||
cmd = bgp_peer.CreatePeer(test_cli20.MyApp(sys.stdout),
|
||||
None)
|
||||
name = 'my-name'
|
||||
my_id = 'my-id'
|
||||
peerip = '1.1.1.1'
|
||||
remote_asnum = '1'
|
||||
args = [name,
|
||||
'--peer-ip', peerip,
|
||||
'--remote-as', remote_asnum, ]
|
||||
position_names = ['name', 'peer_ip', 'remote_as',
|
||||
'auth_type']
|
||||
position_values = [name, peerip, remote_asnum, 'none']
|
||||
self._test_create_resource(resource, cmd, name, my_id, args,
|
||||
position_names, position_values)
|
||||
|
||||
def test_create_bgp_peer_with_all_params(self):
|
||||
# Create BGP peer with all params.
|
||||
resource = 'bgp_peer'
|
||||
cmd = bgp_peer.CreatePeer(test_cli20.MyApp(sys.stdout),
|
||||
None)
|
||||
name = 'my-name'
|
||||
my_id = 'my-id'
|
||||
peerip = '1.1.1.1'
|
||||
remote_asnum = '65535'
|
||||
authType = 'md5'
|
||||
password = 'abc'
|
||||
args = [name,
|
||||
'--peer-ip', peerip,
|
||||
'--remote-as', remote_asnum,
|
||||
'--auth-type', authType,
|
||||
'--password', password]
|
||||
position_names = ['name', 'peer_ip', 'remote_as',
|
||||
'auth_type', 'password']
|
||||
position_values = [name, peerip, remote_asnum, authType, password]
|
||||
self._test_create_resource(resource, cmd, name, my_id, args,
|
||||
position_names, position_values)
|
||||
|
||||
def test_create_bgp_peer_with_invalid_min_remote_asnum(self):
|
||||
# Create BGP peer with invalid minimum remote-asnum.
|
||||
resource = 'bgp_peer'
|
||||
cmd = bgp_peer.CreatePeer(test_cli20.MyApp(sys.stdout),
|
||||
None)
|
||||
name = 'my-name'
|
||||
my_id = 'my-id'
|
||||
peerip = '1.1.1.1'
|
||||
remote_asnum = '0'
|
||||
args = [name,
|
||||
'--peer-ip', peerip,
|
||||
'--remote-as', remote_asnum, ]
|
||||
position_names = ['name', 'peer_ip', 'remote_as', ]
|
||||
position_values = [name, peerip, remote_asnum, ]
|
||||
exc = self.assertRaises(exceptions.CommandError,
|
||||
self._test_create_resource,
|
||||
resource, cmd, name, my_id, args,
|
||||
position_names, position_values)
|
||||
self.assertEqual('remote-as "0" should be an integer [%s:%s].' %
|
||||
(bgp_speaker.MIN_AS_NUM, bgp_speaker.MAX_AS_NUM),
|
||||
str(exc))
|
||||
|
||||
def test_create_bgp_peer_with_invalid_max_remote_asnum(self):
|
||||
# Create BGP peer with invalid maximum remote-asnum.
|
||||
resource = 'bgp_peer'
|
||||
cmd = bgp_peer.CreatePeer(test_cli20.MyApp(sys.stdout),
|
||||
None)
|
||||
name = 'my-name'
|
||||
my_id = 'my-id'
|
||||
peerip = '1.1.1.1'
|
||||
remote_asnum = '65536'
|
||||
args = [name,
|
||||
'--peer-ip', peerip,
|
||||
'--remote-as', remote_asnum, ]
|
||||
position_names = ['name', 'peer_ip', 'remote_as',
|
||||
'auth_type', 'password']
|
||||
position_values = [name, peerip, remote_asnum, 'none', '']
|
||||
exc = self.assertRaises(exceptions.CommandError,
|
||||
self._test_create_resource,
|
||||
resource, cmd, name, my_id, args,
|
||||
position_names, position_values)
|
||||
self.assertEqual('remote-as "65536" should be an integer [%s:%s].' %
|
||||
(bgp_speaker.MIN_AS_NUM, bgp_speaker.MAX_AS_NUM),
|
||||
str(exc))
|
||||
|
||||
def test_create_authenticated_bgp_peer_without_authtype(self):
|
||||
# Create authenticated BGP peer without auth-type.
|
||||
resource = 'bgp_peer'
|
||||
cmd = bgp_peer.CreatePeer(test_cli20.MyApp(sys.stdout),
|
||||
None)
|
||||
name = 'my-name'
|
||||
my_id = 'my-id'
|
||||
peerip = '1.1.1.1'
|
||||
remote_asnum = '2048'
|
||||
password = 'abc'
|
||||
args = [name,
|
||||
'--peer-ip', peerip,
|
||||
'--remote-as', remote_asnum,
|
||||
'--password', password]
|
||||
position_names = ['name', 'peer_ip', 'remote_as', 'password']
|
||||
position_values = [name, peerip, remote_asnum, password]
|
||||
exc = self.assertRaises(exceptions.CommandError,
|
||||
self._test_create_resource,
|
||||
resource, cmd, name, my_id, args,
|
||||
position_names, position_values)
|
||||
self.assertEqual('Must provide auth-type if password is specified.',
|
||||
str(exc))
|
||||
|
||||
def test_create_authenticated_bgp_peer_without_password(self):
|
||||
# Create authenticated BGP peer without password.
|
||||
resource = 'bgp_peer'
|
||||
cmd = bgp_peer.CreatePeer(test_cli20.MyApp(sys.stdout),
|
||||
None)
|
||||
name = 'my-name'
|
||||
my_id = 'my-id'
|
||||
peerip = '1.1.1.1'
|
||||
remote_asnum = '2048'
|
||||
authType = 'md5'
|
||||
args = [name,
|
||||
'--peer-ip', peerip,
|
||||
'--remote-as', remote_asnum,
|
||||
'--auth-type', authType]
|
||||
position_names = ['name', 'peer_ip', 'remote_as', 'auth-type']
|
||||
position_values = [name, peerip, remote_asnum, authType]
|
||||
exc = self.assertRaises(exceptions.CommandError,
|
||||
self._test_create_resource,
|
||||
resource, cmd, name, my_id, args,
|
||||
position_names, position_values)
|
||||
self.assertEqual('Must provide password if auth-type is specified.',
|
||||
str(exc))
|
||||
|
||||
def test_update_bgp_peer(self):
|
||||
# Update BGP peer:
|
||||
# myid --advertise-tenant-networks True
|
||||
# --advertise-floating-ip-host-routes False
|
||||
resource = 'bgp_peer'
|
||||
cmd = bgp_peer.UpdatePeer(test_cli20.MyApp(sys.stdout),
|
||||
None)
|
||||
self._test_update_resource(resource, cmd, 'myid',
|
||||
['myid', '--name', 'new-name',
|
||||
'--password', 'abc'],
|
||||
{'name': 'new-name', 'password': 'abc'})
|
||||
|
||||
def test_update_bgp_peer_exception(self):
|
||||
# Update BGP peer: myid.
|
||||
resource = 'bgp_peer'
|
||||
cmd = bgp_peer.UpdatePeer(test_cli20.MyApp(sys.stdout),
|
||||
None)
|
||||
self.assertRaises(exceptions.CommandError,
|
||||
self._test_update_resource,
|
||||
resource, cmd, 'myid', ['myid'], {})
|
||||
|
||||
def test_list_bgp_peer(self):
|
||||
# List all BGP peers.
|
||||
resources = "bgp_peers"
|
||||
cmd = bgp_peer.ListPeers(test_cli20.MyApp(sys.stdout),
|
||||
None)
|
||||
self._test_list_resources(resources, cmd, True)
|
||||
|
||||
# TODO(Vikram): Add test_list_bgp_peer_pagination
|
||||
|
||||
def test_list_bgp_peer_sort(self):
|
||||
# sorted list: bgp-peer-list --sort-key name --sort-key id
|
||||
# --sort-key asc --sort-key desc
|
||||
resources = "bgp_peers"
|
||||
cmd = bgp_peer.ListPeers(test_cli20.MyApp(sys.stdout),
|
||||
None)
|
||||
self._test_list_resources(resources, cmd,
|
||||
sort_key=["name", "id"],
|
||||
sort_dir=["asc", "desc"])
|
||||
|
||||
def test_list_bgp_peer_limit(self):
|
||||
# size (1000) limited list: bgp-peer-list -P.
|
||||
resources = "bgp_peers"
|
||||
cmd = bgp_peer.ListPeers(test_cli20.MyApp(sys.stdout),
|
||||
None)
|
||||
self._test_list_resources(resources, cmd, page_size=1000)
|
||||
|
||||
def test_show_bgp_peer(self):
|
||||
# Show BGP peer: --fields id --fields name myid.
|
||||
resource = 'bgp_peer'
|
||||
cmd = bgp_peer.ShowPeer(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_bgp_peer(self):
|
||||
# Delete BGP peer: bgp_peer_id.
|
||||
resource = 'bgp_peer'
|
||||
cmd = bgp_peer.DeletePeer(test_cli20.MyApp(sys.stdout),
|
||||
None)
|
||||
myid = 'myid'
|
||||
args = [myid]
|
||||
self._test_delete_resource(resource, cmd, myid, args)
|
||||
267
neutronclient/tests/unit/bgp/test_cli20_speaker.py
Normal file
267
neutronclient/tests/unit/bgp/test_cli20_speaker.py
Normal file
@@ -0,0 +1,267 @@
|
||||
# Copyright 2016 Huawei Technologies India Pvt. Ltd.
|
||||
# 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 mox3 import mox
|
||||
|
||||
from neutronclient.common import exceptions
|
||||
from neutronclient.neutron.v2_0.bgp import speaker as bgp_speaker
|
||||
from neutronclient.tests.unit import test_cli20
|
||||
|
||||
|
||||
class CLITestV20BGPSpeakerJSON(test_cli20.CLITestV20Base):
|
||||
|
||||
non_admin_status_resources = ['bgp_speaker']
|
||||
|
||||
def test_create_bgp_speaker_with_minimal_options(self):
|
||||
# Create BGP Speaker with mandatory params.
|
||||
resource = 'bgp_speaker'
|
||||
cmd = bgp_speaker.CreateSpeaker(test_cli20.MyApp(sys.stdout),
|
||||
None)
|
||||
name = 'my-name'
|
||||
my_id = 'my-id'
|
||||
local_asnum = '1'
|
||||
args = [name, '--local-as', local_asnum, ]
|
||||
position_names = ['name', 'local_as', 'ip_version']
|
||||
position_values = [name, local_asnum, 4]
|
||||
self._test_create_resource(resource, cmd, name, my_id, args,
|
||||
position_names, position_values)
|
||||
|
||||
def test_create_ipv4_bgp_speaker_with_all_params(self):
|
||||
# Create BGP Speaker with all params.
|
||||
resource = 'bgp_speaker'
|
||||
cmd = bgp_speaker.CreateSpeaker(test_cli20.MyApp(sys.stdout),
|
||||
None)
|
||||
name = 'my-name'
|
||||
my_id = 'my-id'
|
||||
local_asnum = '1'
|
||||
args = [name,
|
||||
'--local-as', local_asnum,
|
||||
'--ip-version', '4',
|
||||
'--advertise-floating-ip-host-routes', 'True',
|
||||
'--advertise-tenant-networks', 'True']
|
||||
position_names = ['name', 'local_as', 'ip_version',
|
||||
'advertise_floating_ip_host_routes',
|
||||
'advertise_tenant_networks']
|
||||
position_values = [name, local_asnum, 4, 'True', 'True']
|
||||
self._test_create_resource(resource, cmd, name, my_id, args,
|
||||
position_names, position_values)
|
||||
|
||||
def test_create_ipv6_bgp_speaker_with_all_params(self):
|
||||
# Create BGP Speaker with all params.
|
||||
resource = 'bgp_speaker'
|
||||
cmd = bgp_speaker.CreateSpeaker(test_cli20.MyApp(sys.stdout),
|
||||
None)
|
||||
name = 'my-name'
|
||||
my_id = 'my-id'
|
||||
local_asnum = '65535'
|
||||
args = [name,
|
||||
'--local-as', local_asnum,
|
||||
'--ip-version', '6',
|
||||
'--advertise-floating-ip-host-routes', 'True',
|
||||
'--advertise-tenant-networks', 'True']
|
||||
position_names = ['name', 'local_as', 'ip_version',
|
||||
'advertise_floating_ip_host_routes',
|
||||
'advertise_tenant_networks']
|
||||
position_values = [name, local_asnum, 6, 'True', 'True']
|
||||
self._test_create_resource(resource, cmd, name, my_id, args,
|
||||
position_names, position_values)
|
||||
|
||||
def test_create_bgp_speaker_with_invalid_min_local_asnum(self):
|
||||
# Create BGP Speaker with invalid minimum local-asnum.
|
||||
resource = 'bgp_speaker'
|
||||
cmd = bgp_speaker.CreateSpeaker(test_cli20.MyApp(sys.stdout),
|
||||
None)
|
||||
name = 'my-name'
|
||||
my_id = 'my-id'
|
||||
local_asnum = '0'
|
||||
args = [name,
|
||||
'--local-as', local_asnum]
|
||||
position_names = ['name', 'local_as']
|
||||
position_values = [name, local_asnum]
|
||||
exc = self.assertRaises(exceptions.CommandError,
|
||||
self._test_create_resource,
|
||||
resource, cmd, name, my_id, args,
|
||||
position_names, position_values)
|
||||
self.assertEqual('local-as "0" should be an integer [%s:%s].' %
|
||||
(bgp_speaker.MIN_AS_NUM, bgp_speaker.MAX_AS_NUM),
|
||||
str(exc))
|
||||
|
||||
def test_create_bgp_speaker_with_invalid_max_local_asnum(self):
|
||||
# Create BGP Speaker with invalid maximum local-asnum.
|
||||
resource = 'bgp_speaker'
|
||||
cmd = bgp_speaker.CreateSpeaker(test_cli20.MyApp(sys.stdout),
|
||||
None)
|
||||
name = 'my-name'
|
||||
my_id = 'my-id'
|
||||
local_asnum = '65536'
|
||||
args = [name,
|
||||
'--local-as', local_asnum]
|
||||
position_names = ['name', 'local_as', ]
|
||||
position_values = [name, local_asnum, ]
|
||||
exc = self.assertRaises(exceptions.CommandError,
|
||||
self._test_create_resource,
|
||||
resource, cmd, name, my_id, args,
|
||||
position_names, position_values)
|
||||
self.assertEqual('local-as "65536" should be an integer [%s:%s].' %
|
||||
(bgp_speaker.MIN_AS_NUM, bgp_speaker.MAX_AS_NUM),
|
||||
str(exc))
|
||||
|
||||
def test_update_bgp_speaker(self):
|
||||
# Update BGP Speaker:
|
||||
# myid --advertise-tenant-networks True
|
||||
# --advertise-floating-ip-host-routes False
|
||||
resource = 'bgp_speaker'
|
||||
cmd = bgp_speaker.UpdateSpeaker(test_cli20.MyApp(sys.stdout),
|
||||
None)
|
||||
self._test_update_resource(resource, cmd, 'myid',
|
||||
['myid',
|
||||
'--name', 'new-name',
|
||||
'--advertise-tenant-networks', 'True',
|
||||
'--advertise-floating-ip-host-routes',
|
||||
'False'],
|
||||
{'name': 'new-name',
|
||||
'advertise_tenant_networks': 'True',
|
||||
'advertise_floating_ip_host_routes':
|
||||
'False'})
|
||||
|
||||
def test_update_bgp_speaker_exception(self):
|
||||
# Update BGP Speaker: myid.
|
||||
resource = 'bgp_speaker'
|
||||
cmd = bgp_speaker.UpdateSpeaker(test_cli20.MyApp(sys.stdout),
|
||||
None)
|
||||
self.assertRaises(exceptions.CommandError,
|
||||
self._test_update_resource,
|
||||
resource, cmd, 'myid', ['myid'], {})
|
||||
|
||||
def test_list_bgp_speaker(self):
|
||||
# List all BGP Speakers.
|
||||
resources = "bgp_speakers"
|
||||
cmd = bgp_speaker.ListSpeakers(test_cli20.MyApp(sys.stdout),
|
||||
None)
|
||||
self._test_list_resources(resources, cmd, True)
|
||||
|
||||
def test_list_bgp_speaker_pagination(self):
|
||||
# List all BGP Speakers with pagination support.
|
||||
cmd = bgp_speaker.ListSpeakers(test_cli20.MyApp(sys.stdout),
|
||||
None)
|
||||
self.mox.StubOutWithMock(bgp_speaker.ListSpeakers,
|
||||
"extend_list")
|
||||
bgp_speaker.ListSpeakers.extend_list(mox.IsA(list),
|
||||
mox.IgnoreArg())
|
||||
self._test_list_resources_with_pagination("bgp_speakers",
|
||||
cmd)
|
||||
self.mox.VerifyAll()
|
||||
self.mox.UnsetStubs()
|
||||
|
||||
def test_list_bgp_speaker_sort(self):
|
||||
# sorted list: bgp-speaker-list --sort-key name --sort-key id
|
||||
# --sort-key asc --sort-key desc
|
||||
resources = "bgp_speakers"
|
||||
cmd = bgp_speaker.ListSpeakers(test_cli20.MyApp(sys.stdout),
|
||||
None)
|
||||
self._test_list_resources(resources, cmd,
|
||||
sort_key=["name", "id"],
|
||||
sort_dir=["asc", "desc"])
|
||||
|
||||
def test_list_bgp_speaker_limit(self):
|
||||
# size (1000) limited list: bgp-speaker-list -P.
|
||||
resources = "bgp_speakers"
|
||||
cmd = bgp_speaker.ListSpeakers(test_cli20.MyApp(sys.stdout),
|
||||
None)
|
||||
self._test_list_resources(resources, cmd, page_size=1000)
|
||||
|
||||
def test_show_bgp_speaker(self):
|
||||
# Show BGP Speaker: --fields id --fields name myid.
|
||||
resource = 'bgp_speaker'
|
||||
cmd = bgp_speaker.ShowSpeaker(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_bgp_speaker(self):
|
||||
# Delete BGP Speaker: bgp_speaker_id.
|
||||
resource = 'bgp_speaker'
|
||||
cmd = bgp_speaker.DeleteSpeaker(test_cli20.MyApp(sys.stdout),
|
||||
None)
|
||||
myid = 'myid'
|
||||
args = [myid]
|
||||
self._test_delete_resource(resource, cmd, myid, args)
|
||||
|
||||
def _test_add_remove_peer(self, action, cmd, args):
|
||||
"""Add or Remove BGP Peer to/from a BGP Speaker."""
|
||||
resource = 'bgp_speaker'
|
||||
subcmd = '%s_bgp_peer' % action
|
||||
body = {'bgp_peer_id': 'peerid'}
|
||||
if action == 'add':
|
||||
retval = {'bgp_peer': 'peerid'}
|
||||
else:
|
||||
retval = None
|
||||
self._test_update_resource_action(resource, cmd, 'myid',
|
||||
subcmd, args, body, retval)
|
||||
|
||||
def test_add_peer_to_bgp_speaker(self):
|
||||
# Add peer to BGP speaker: myid peer_id=peerid
|
||||
cmd = bgp_speaker.AddPeerToSpeaker(test_cli20.MyApp(sys.stdout),
|
||||
None)
|
||||
args = ['myid', 'peerid']
|
||||
self._test_add_remove_peer('add', cmd, args)
|
||||
|
||||
def test_remove_peer_from_bgp_speaker(self):
|
||||
# Remove peer from BGP speaker: myid peer_id=peerid
|
||||
cmd = bgp_speaker.RemovePeerFromSpeaker(test_cli20.MyApp(sys.stdout),
|
||||
None)
|
||||
args = ['myid', 'peerid']
|
||||
self._test_add_remove_peer('remove', cmd, args)
|
||||
|
||||
def _test_add_remove_network(self, action, cmd, args):
|
||||
# Add or Remove network to/from a BGP Speaker.
|
||||
resource = 'bgp_speaker'
|
||||
subcmd = '%s_gateway_network' % action
|
||||
body = {'network_id': 'netid'}
|
||||
if action == 'add':
|
||||
retval = {'network': 'netid'}
|
||||
else:
|
||||
retval = None
|
||||
self._test_update_resource_action(resource, cmd, 'myid',
|
||||
subcmd, args, body, retval)
|
||||
|
||||
def test_add_network_to_bgp_speaker(self):
|
||||
# Add peer to BGP speaker: myid network_id=netid
|
||||
cmd = bgp_speaker.AddNetworkToSpeaker(test_cli20.MyApp(sys.stdout),
|
||||
None)
|
||||
args = ['myid', 'netid']
|
||||
self._test_add_remove_network('add', cmd, args)
|
||||
|
||||
def test_remove_network_from_bgp_speaker(self):
|
||||
# Remove network from BGP speaker: myid network_id=netid
|
||||
cmd = bgp_speaker.RemoveNetworkFromSpeaker(
|
||||
test_cli20.MyApp(sys.stdout), None)
|
||||
args = ['myid', 'netid']
|
||||
self._test_add_remove_network('remove', cmd, args)
|
||||
|
||||
def test_list_routes_advertised_by_a_bgp_speaker(self):
|
||||
# Retrieve advertised route list
|
||||
resources = 'advertised_routes'
|
||||
cmd = bgp_speaker.ListRoutesAdvertisedBySpeaker(
|
||||
test_cli20.MyApp(sys.stdout), None)
|
||||
bs_id = 'bgp_speaker_id1'
|
||||
path = ((self.client.bgp_speaker_path + '/get_advertised_routes') %
|
||||
bs_id)
|
||||
self._test_list_resources(resources, cmd, base_args=[bs_id],
|
||||
path=path)
|
||||
@@ -160,3 +160,11 @@ class CLITestV20FirewallJSON(test_cli20.CLITestV20Base):
|
||||
my_id = 'my-id'
|
||||
args = [my_id]
|
||||
self._test_delete_resource(resource, cmd, my_id, args)
|
||||
|
||||
def test_update_firewall_admin_state(self):
|
||||
# firewall-update myid --admin-state-up True.
|
||||
resource = 'firewall'
|
||||
cmd = firewall.UpdateFirewall(test_cli20.MyApp(sys.stdout), None)
|
||||
self._test_update_resource(resource, cmd, 'myid',
|
||||
['myid', '--admin-state-up', 'True'],
|
||||
{'admin_state_up': 'True'})
|
||||
|
||||
@@ -213,3 +213,14 @@ class CLITestV20FirewallPolicyJSON(test_cli20.CLITestV20Base):
|
||||
shell.run_command(cmd, cmd_parser, args)
|
||||
self.mox.VerifyAll()
|
||||
self.mox.UnsetStubs()
|
||||
|
||||
def test_update_firewall_policy_name_shared_audited(self):
|
||||
# firewall-policy-update myid --name newname2 --shared --audited
|
||||
resource = 'firewall_policy'
|
||||
cmd = firewallpolicy.UpdateFirewallPolicy(test_cli20.MyApp(sys.stdout),
|
||||
None)
|
||||
self._test_update_resource(resource, cmd, 'myid',
|
||||
['myid', '--name', 'newname2',
|
||||
'--shared', 'True', '--audited', 'True'],
|
||||
{'name': 'newname2',
|
||||
'shared': 'True', 'audited': 'True'})
|
||||
|
||||
@@ -32,6 +32,7 @@ class CLITestV20FirewallRuleJSON(test_cli20.CLITestV20Base):
|
||||
my_id = 'myid'
|
||||
protocol = 'tcp'
|
||||
action = 'allow'
|
||||
ip_version = 4
|
||||
args = ['--tenant-id', tenant_id,
|
||||
'--admin-state-up',
|
||||
'--protocol', protocol,
|
||||
@@ -42,7 +43,8 @@ class CLITestV20FirewallRuleJSON(test_cli20.CLITestV20Base):
|
||||
self._test_create_resource(resource, cmd, name, my_id, args,
|
||||
position_names, position_values,
|
||||
protocol=protocol, action=action,
|
||||
enabled=enabled, tenant_id=tenant_id)
|
||||
enabled=enabled, tenant_id=tenant_id,
|
||||
ip_version=ip_version)
|
||||
|
||||
def test_create_enabled_firewall_rule_with_mandatory_params_lcase(self):
|
||||
self._test_create_firewall_rule_with_mandatory_params(enabled='true')
|
||||
@@ -56,7 +58,9 @@ class CLITestV20FirewallRuleJSON(test_cli20.CLITestV20Base):
|
||||
def test_create_disabled_firewall_rule_with_mandatory_params(self):
|
||||
self._test_create_firewall_rule_with_mandatory_params(enabled='False')
|
||||
|
||||
def _setup_create_firewall_rule_with_all_params(self, protocol='tcp'):
|
||||
def _setup_create_firewall_rule_with_all_params(
|
||||
self, protocol='tcp', protocol_cli=None,
|
||||
action='allow', action_cli=None, ip_version='4'):
|
||||
# firewall-rule-create with all params set.
|
||||
resource = 'firewall_rule'
|
||||
cmd = firewallrule.CreateFirewallRule(test_cli20.MyApp(sys.stdout),
|
||||
@@ -67,18 +71,18 @@ class CLITestV20FirewallRuleJSON(test_cli20.CLITestV20Base):
|
||||
destination_ip = '192.168.2.0/24'
|
||||
source_port = '0:65535'
|
||||
destination_port = '0:65535'
|
||||
action = 'allow'
|
||||
tenant_id = 'my-tenant'
|
||||
my_id = 'myid'
|
||||
enabled = 'True'
|
||||
args = ['--description', description,
|
||||
'--shared',
|
||||
'--protocol', protocol,
|
||||
'--protocol', protocol_cli or protocol,
|
||||
'--ip-version', ip_version,
|
||||
'--source-ip-address', source_ip,
|
||||
'--destination-ip-address', destination_ip,
|
||||
'--source-port', source_port,
|
||||
'--destination-port', destination_port,
|
||||
'--action', action,
|
||||
'--action', action_cli or action,
|
||||
'--enabled', enabled,
|
||||
'--admin-state-up',
|
||||
'--tenant-id', tenant_id]
|
||||
@@ -86,16 +90,29 @@ class CLITestV20FirewallRuleJSON(test_cli20.CLITestV20Base):
|
||||
position_values = []
|
||||
if protocol == 'any':
|
||||
protocol = None
|
||||
self._test_create_resource(resource, cmd, name, my_id, args,
|
||||
position_names, position_values,
|
||||
description=description, shared=True,
|
||||
protocol=protocol,
|
||||
source_ip_address=source_ip,
|
||||
destination_ip_address=destination_ip,
|
||||
source_port=source_port,
|
||||
destination_port=destination_port,
|
||||
action=action, enabled='True',
|
||||
tenant_id=tenant_id)
|
||||
if ip_version == '4' or ip_version == '6':
|
||||
self._test_create_resource(resource, cmd, name, my_id, args,
|
||||
position_names, position_values,
|
||||
description=description, shared=True,
|
||||
protocol=protocol,
|
||||
ip_version=int(ip_version),
|
||||
source_ip_address=source_ip,
|
||||
destination_ip_address=destination_ip,
|
||||
source_port=source_port,
|
||||
destination_port=destination_port,
|
||||
action=action, enabled='True',
|
||||
tenant_id=tenant_id)
|
||||
else:
|
||||
self.assertRaises(SystemExit, self._test_create_resource,
|
||||
resource, cmd, name, my_id, args,
|
||||
position_names, position_values,
|
||||
ip_version=int(ip_version),
|
||||
source_ip_address=source_ip,
|
||||
destination_ip_address=destination_ip,
|
||||
source_port=source_port,
|
||||
destination_port=destination_port,
|
||||
action=action, enabled='True',
|
||||
tenant_id=tenant_id)
|
||||
|
||||
def test_create_firewall_rule_with_all_params(self):
|
||||
self._setup_create_firewall_rule_with_all_params()
|
||||
@@ -103,6 +120,22 @@ class CLITestV20FirewallRuleJSON(test_cli20.CLITestV20Base):
|
||||
def test_create_firewall_rule_with_proto_any(self):
|
||||
self._setup_create_firewall_rule_with_all_params(protocol='any')
|
||||
|
||||
def test_create_firewall_rule_with_IP_version_6(self):
|
||||
self._setup_create_firewall_rule_with_all_params(ip_version='6')
|
||||
|
||||
def test_create_firewall_rule_with_invalid_IP_version(self):
|
||||
self._setup_create_firewall_rule_with_all_params(ip_version='5')
|
||||
|
||||
def test_create_firewall_rule_with_proto_action_upper_capitalized(self):
|
||||
for protocol in ('TCP', 'Tcp', 'ANY', 'AnY'):
|
||||
self._setup_create_firewall_rule_with_all_params(
|
||||
protocol=protocol.lower(),
|
||||
protocol_cli=protocol)
|
||||
for action in ('Allow', 'DENY', 'reject'):
|
||||
self._setup_create_firewall_rule_with_all_params(
|
||||
action=action.lower(),
|
||||
action_cli=action)
|
||||
|
||||
def test_list_firewall_rules(self):
|
||||
# firewall-rule-list.
|
||||
resources = "firewall_rules"
|
||||
@@ -160,15 +193,54 @@ class CLITestV20FirewallRuleJSON(test_cli20.CLITestV20Base):
|
||||
['myid', '--name', 'newname'],
|
||||
{'name': 'newname', })
|
||||
|
||||
def test_update_firewall_rule_protocol(self):
|
||||
# firewall-rule-update myid --protocol any.
|
||||
resource = 'firewall_rule'
|
||||
cmd = firewallrule.UpdateFirewallRule(test_cli20.MyApp(sys.stdout),
|
||||
None)
|
||||
self._test_update_resource(resource, cmd, 'myid',
|
||||
['myid', '--protocol', 'any'],
|
||||
{'protocol': None, })
|
||||
|
||||
# firewall-rule-update myid --description any
|
||||
self._test_update_resource(resource, cmd, 'myid',
|
||||
['myid', '--description', 'any'],
|
||||
{'description': 'any', })
|
||||
|
||||
# firewall-rule-update myid --source_ip_address 192.192.192.192
|
||||
self._test_update_resource(resource, cmd, 'myid',
|
||||
['myid', '--source_ip_address',
|
||||
'192.192.192.192'],
|
||||
{'source_ip_address': '192.192.192.192', })
|
||||
|
||||
# firewall-rule-update myid --source_port 32767
|
||||
self._test_update_resource(resource, cmd, 'myid',
|
||||
['myid', '--source_port', '32767'],
|
||||
{'source_port': '32767', })
|
||||
|
||||
# firewall-rule-update myid --destination_ip_address 0.1.0.1
|
||||
self._test_update_resource(resource, cmd, 'myid',
|
||||
['myid', '--destination_ip_address',
|
||||
'0.1.0.1'],
|
||||
{'destination_ip_address': '0.1.0.1', })
|
||||
|
||||
# firewall-rule-update myid --destination_port 65432
|
||||
self._test_update_resource(resource, cmd, 'myid',
|
||||
['myid', '--destination_port',
|
||||
'65432'],
|
||||
{'destination_port': '65432', })
|
||||
|
||||
# firewall-rule-update myid --enabled False
|
||||
self._test_update_resource(resource, cmd, 'myid',
|
||||
['myid', '--enabled', 'False'],
|
||||
{'enabled': 'False', })
|
||||
|
||||
# firewall-rule-update myid --action reject
|
||||
self._test_update_resource(resource, cmd, 'myid',
|
||||
['myid', '--action', 'reject'],
|
||||
{'action': 'reject', })
|
||||
|
||||
# firewall-rule-update myid --shared false
|
||||
self._test_update_resource(resource, cmd, 'myid',
|
||||
['myid', '--shared', 'false'],
|
||||
{'shared': 'false', })
|
||||
|
||||
def test_delete_firewall_rule(self):
|
||||
# firewall-rule-delete my-id.
|
||||
resource = 'firewall_rule'
|
||||
|
||||
260
neutronclient/tests/unit/lb/v2/test_cli20_l7policy.py
Normal file
260
neutronclient/tests/unit/lb/v2/test_cli20_l7policy.py
Normal file
@@ -0,0 +1,260 @@
|
||||
# Copyright 2016 Radware LTD.
|
||||
# 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 neutronclient.common import exceptions
|
||||
from neutronclient.neutron.v2_0.lb.v2 import l7policy
|
||||
from neutronclient.tests.unit import test_cli20
|
||||
|
||||
"""Structure for mapping cli and api arguments
|
||||
|
||||
The structure maps cli arguments and a list of its
|
||||
api argument name, default cli value and default api value.
|
||||
It helps to make tests more general for different argument types.
|
||||
"""
|
||||
args_conf = {
|
||||
'name': ['name', 'test_policy', 'test_policy'],
|
||||
'description': ['description', 'test policy', 'test policy'],
|
||||
'listener': ['listener_id', 'test_listener', 'test_listener'],
|
||||
'admin-state-up': ['admin_state_up', True, True],
|
||||
'admin-state-down': ['admin_state_up', None, False],
|
||||
'action': ['action', 'REJECT', 'REJECT'],
|
||||
'redirect-url': ['redirect_url', 'http://url', 'http://url'],
|
||||
'redirect-pool': ['redirect_pool_id', 'test_pool', 'test_pool'],
|
||||
'position': ['position', '1', 1]}
|
||||
|
||||
|
||||
class CLITestV20LbL7PolicyJSON(test_cli20.CLITestV20Base):
|
||||
|
||||
def _get_test_args(self, *args, **kwargs):
|
||||
"""Function for generically building testing arguments"""
|
||||
cli_args = []
|
||||
api_args = {}
|
||||
for arg in args:
|
||||
cli_args.append('--' + arg)
|
||||
if not args_conf[arg][1]:
|
||||
pass
|
||||
elif arg in kwargs:
|
||||
cli_args.append(str(kwargs[arg]))
|
||||
else:
|
||||
cli_args.append(args_conf[arg][1])
|
||||
|
||||
if arg in kwargs:
|
||||
api_args[args_conf[arg][0]] = kwargs[arg]
|
||||
else:
|
||||
api_args[args_conf[arg][0]] = args_conf[arg][2]
|
||||
|
||||
return cli_args, api_args
|
||||
|
||||
def _test_create_policy(self, *args, **kwargs):
|
||||
resource = 'l7policy'
|
||||
cmd_resource = 'lbaas_l7policy'
|
||||
cmd = l7policy.CreateL7Policy(test_cli20.MyApp(sys.stdout), None)
|
||||
cli_args, api_args = self._get_test_args(*args, **kwargs)
|
||||
position_names = list(api_args.keys())
|
||||
position_values = list(api_args.values())
|
||||
self._test_create_resource(resource, cmd, None, 'test_id',
|
||||
cli_args, position_names, position_values,
|
||||
cmd_resource=cmd_resource)
|
||||
|
||||
def _test_update_policy(self, *args, **kwargs):
|
||||
resource = 'l7policy'
|
||||
cmd_resource = 'lbaas_l7policy'
|
||||
cmd = l7policy.UpdateL7Policy(test_cli20.MyApp(sys.stdout), None)
|
||||
cli_args, api_args = self._get_test_args(*args, **kwargs)
|
||||
cli_args.append('test_id')
|
||||
self._test_update_resource(resource, cmd, 'test_id',
|
||||
cli_args, api_args,
|
||||
cmd_resource=cmd_resource)
|
||||
|
||||
def test_create_policy_with_mandatory_params(self):
|
||||
# lbaas-l7policy-create with mandatory params only.
|
||||
self._test_create_policy('action', 'listener')
|
||||
|
||||
def test_create_policy_with_all_params(self):
|
||||
# lbaas-l7policy-create REJECT policy.
|
||||
self._test_create_policy('name', 'description',
|
||||
'action', 'listener',
|
||||
'position')
|
||||
|
||||
def test_create_disabled_policy(self):
|
||||
# lbaas-l7policy-create disabled REJECT policy.
|
||||
self._test_create_policy('action', 'listener', 'admin-state-down')
|
||||
|
||||
def test_create_url_redirect_policy(self):
|
||||
# lbaas-l7policy-create REDIRECT_TO_URL policy.
|
||||
self._test_create_policy('name', 'description',
|
||||
'action', 'listener',
|
||||
'redirect-url',
|
||||
action='REDIRECT_TO_URL')
|
||||
|
||||
def test_create_url_redirect_policy_no_url(self):
|
||||
# lbaas-l7policy-create REDIRECT_TO_URL policy without url argument.
|
||||
self.assertRaises(exceptions.CommandError,
|
||||
self._test_create_policy,
|
||||
'name', 'description',
|
||||
'action', 'listener',
|
||||
action='REDIRECT_TO_URL')
|
||||
|
||||
def test_create_pool_redirect_policy(self):
|
||||
# lbaas-l7policy-create REDIRECT_TO_POOL policy.
|
||||
self._test_create_policy('name', 'description',
|
||||
'action', 'listener',
|
||||
'redirect-pool',
|
||||
action='REDIRECT_TO_POOL')
|
||||
|
||||
def test_create_pool_redirect_policy_no_pool(self):
|
||||
# lbaas-l7policy-create REDIRECT_TO_POOL policy without pool argument.
|
||||
self.assertRaises(exceptions.CommandError,
|
||||
self._test_create_policy,
|
||||
'name', 'description',
|
||||
'action', 'listener',
|
||||
action='REDIRECT_TO_POOL')
|
||||
|
||||
def test_create_reject_policy_with_url(self):
|
||||
# lbaas-l7policy-create REJECT policy while specifying url argument.
|
||||
self.assertRaises(exceptions.CommandError,
|
||||
self._test_create_policy,
|
||||
'action', 'listener',
|
||||
'redirect-url')
|
||||
|
||||
def test_create_reject_policy_with_pool(self):
|
||||
# lbaas-l7policy-create REJECT policy while specifying pool argument.
|
||||
self.assertRaises(exceptions.CommandError,
|
||||
self._test_create_policy,
|
||||
'action', 'listener',
|
||||
'redirect-pool')
|
||||
|
||||
def test_create_pool_redirect_policy_with_url(self):
|
||||
# lbaas-l7policy-create REDIRECT_TO_POOL policy with url argument.
|
||||
self.assertRaises(exceptions.CommandError,
|
||||
self._test_create_policy,
|
||||
'action', 'listener',
|
||||
'redirect-pool', 'redirect-url',
|
||||
action='REDIRECT_TO_POOL')
|
||||
|
||||
def test_create_url_redirect_policy_with_pool(self):
|
||||
# lbaas-l7policy-create REDIRECT_TO_URL policy with pool argument.
|
||||
self.assertRaises(exceptions.CommandError,
|
||||
self._test_create_policy,
|
||||
'action', 'listener',
|
||||
'redirect-pool', 'redirect-url',
|
||||
action='REDIRECT_TO_URL')
|
||||
|
||||
def test_list_policies(self):
|
||||
# lbaas-l7policy-list.
|
||||
|
||||
resources = 'l7policies'
|
||||
cmd_resources = 'lbaas_l7policies'
|
||||
cmd = l7policy.ListL7Policy(test_cli20.MyApp(sys.stdout), None)
|
||||
self._test_list_resources(resources, cmd, True,
|
||||
cmd_resources=cmd_resources)
|
||||
|
||||
def test_list_policies_pagination(self):
|
||||
# lbaas-l7policy-list with pagination.
|
||||
|
||||
resources = 'l7policies'
|
||||
cmd_resources = 'lbaas_l7policies'
|
||||
cmd = l7policy.ListL7Policy(test_cli20.MyApp(sys.stdout), None)
|
||||
self._test_list_resources_with_pagination(
|
||||
resources, cmd, cmd_resources=cmd_resources)
|
||||
|
||||
def test_list_policies_sort(self):
|
||||
# lbaas-l7policy-list --sort-key id --sort-key asc.
|
||||
|
||||
resources = 'l7policies'
|
||||
cmd_resources = 'lbaas_l7policies'
|
||||
cmd = l7policy.ListL7Policy(test_cli20.MyApp(sys.stdout), None)
|
||||
self._test_list_resources(
|
||||
resources, cmd, True, cmd_resources=cmd_resources)
|
||||
|
||||
def test_list_policies_limit(self):
|
||||
# lbaas-l7policy-list -P.
|
||||
|
||||
resources = 'l7policies'
|
||||
cmd_resources = 'lbaas_l7policies'
|
||||
cmd = l7policy.ListL7Policy(test_cli20.MyApp(sys.stdout), None)
|
||||
self._test_list_resources(
|
||||
resources, cmd, page_size=1000, cmd_resources=cmd_resources)
|
||||
|
||||
def test_show_policy_id(self):
|
||||
# lbaas-l7policy-show test_id.
|
||||
|
||||
resource = 'l7policy'
|
||||
cmd_resource = 'lbaas_l7policy'
|
||||
cmd = l7policy.ShowL7Policy(test_cli20.MyApp(sys.stdout), None)
|
||||
args = ['--fields', 'test_id', self.test_id]
|
||||
self._test_show_resource(
|
||||
resource, cmd, self.test_id, args,
|
||||
['test_id'], cmd_resource=cmd_resource)
|
||||
|
||||
def test_show_policy_id_name(self):
|
||||
# lbaas-l7policy-show.
|
||||
|
||||
resource = 'l7policy'
|
||||
cmd_resource = 'lbaas_l7policy'
|
||||
cmd = l7policy.ShowL7Policy(test_cli20.MyApp(sys.stdout), None)
|
||||
args = ['--fields', 'test_id', '--fields', 'name', self.test_id]
|
||||
self._test_show_resource(
|
||||
resource, cmd, self.test_id, args,
|
||||
['test_id', 'name'], cmd_resource=cmd_resource)
|
||||
|
||||
def test_disable_policy(self):
|
||||
# lbaas-l7policy-update test_id --admin-state-up False.
|
||||
|
||||
self._test_update_policy('admin-state-up',
|
||||
**{'admin-state-up': 'False'})
|
||||
|
||||
def test_update_policy_name_and_description(self):
|
||||
# lbaas-l7policy-update test_id --name other --description other_desc.
|
||||
|
||||
self._test_update_policy('name', 'description',
|
||||
name='name',
|
||||
description='other desc')
|
||||
|
||||
def test_update_pool_redirect_policy(self):
|
||||
# lbaas-l7policy-update test_id --action REDIRECT_TO_POOL
|
||||
# --redirect-pool id.
|
||||
|
||||
self._test_update_policy('action', 'redirect-pool',
|
||||
**{'action': 'REDIRECT_TO_POOL',
|
||||
'redirect-pool': 'id'})
|
||||
|
||||
def test_update_url_redirect_policy(self):
|
||||
# lbaas-l7policy-update test_id --action REDIRECT_TO_URL
|
||||
# --redirect-url http://other_url.
|
||||
|
||||
self._test_update_policy('action', 'redirect-url',
|
||||
**{'action': 'REDIRECT_TO_URL',
|
||||
'redirect-url': 'http://other_url'})
|
||||
|
||||
def test_update_policy_position(self):
|
||||
# lbaas-l7policy-update test_id --position 2.
|
||||
|
||||
self._test_update_policy('position',
|
||||
position=2)
|
||||
|
||||
def test_delete_policy(self):
|
||||
# lbaas-l7policy-delete test_id.
|
||||
|
||||
resource = 'l7policy'
|
||||
cmd_resource = 'lbaas_l7policy'
|
||||
cmd = l7policy.DeleteL7Policy(test_cli20.MyApp(sys.stdout), None)
|
||||
test_id = 'test_id'
|
||||
args = [test_id]
|
||||
self._test_delete_resource(resource, cmd, test_id, args,
|
||||
cmd_resource=cmd_resource)
|
||||
210
neutronclient/tests/unit/lb/v2/test_cli20_l7rule.py
Normal file
210
neutronclient/tests/unit/lb/v2/test_cli20_l7rule.py
Normal file
@@ -0,0 +1,210 @@
|
||||
# Copyright 2016 Radware LTD.
|
||||
# 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 neutronclient.neutron.v2_0.lb.v2 import l7rule
|
||||
from neutronclient.tests.unit import test_cli20
|
||||
|
||||
|
||||
"""Structure for mapping cli and api arguments
|
||||
|
||||
The structure maps cli arguments and a list of its
|
||||
api argument name, default cli value and default api value.
|
||||
It helps to make tests more general for different argument types.
|
||||
"""
|
||||
args_conf = {
|
||||
'admin-state-up': ['admin_state_up', True, True],
|
||||
'admin-state-down': ['admin_state_up', None, False],
|
||||
'type': ['type', 'HOST_NAME', 'HOST_NAME'],
|
||||
'compare-type': ['compare_type', 'EQUAL_TO', 'EQUAL_TO'],
|
||||
'invert-compare': ['invert', None, True],
|
||||
'key': ['key', 'key', 'key'],
|
||||
'value': ['value', 'value', 'value']}
|
||||
|
||||
|
||||
class CLITestV20LbL7RuleJSON(test_cli20.CLITestV20Base):
|
||||
|
||||
def _get_test_args(self, *args, **kwargs):
|
||||
"""Function for generically building testing arguments"""
|
||||
cli_args = []
|
||||
api_args = {}
|
||||
for arg in args:
|
||||
cli_args.append('--' + arg)
|
||||
if not args_conf[arg][1]:
|
||||
pass
|
||||
elif arg in kwargs:
|
||||
cli_args.append(str(kwargs[arg]))
|
||||
else:
|
||||
cli_args.append(args_conf[arg][1])
|
||||
|
||||
if arg in kwargs:
|
||||
api_args[args_conf[arg][0]] = kwargs[arg]
|
||||
else:
|
||||
api_args[args_conf[arg][0]] = args_conf[arg][2]
|
||||
|
||||
if 'invert' not in api_args:
|
||||
api_args['invert'] = False
|
||||
return cli_args, api_args
|
||||
|
||||
def _test_create_rule(self, *args, **kwargs):
|
||||
resource = 'rule'
|
||||
cmd_resource = 'lbaas_l7rule'
|
||||
cmd = l7rule.CreateL7Rule(test_cli20.MyApp(sys.stdout), None)
|
||||
cli_args, api_args = self._get_test_args(*args, **kwargs)
|
||||
position_names = list(api_args.keys())
|
||||
position_values = list(api_args.values())
|
||||
cli_args.append('test_policy')
|
||||
self._test_create_resource(resource, cmd, None, 'test_id',
|
||||
cli_args, position_names, position_values,
|
||||
cmd_resource=cmd_resource,
|
||||
parent_id='test_policy')
|
||||
|
||||
def _test_update_rule(self, *args, **kwargs):
|
||||
resource = 'rule'
|
||||
cmd_resource = 'lbaas_l7rule'
|
||||
cmd = l7rule.UpdateL7Rule(test_cli20.MyApp(sys.stdout), None)
|
||||
cli_args, api_args = self._get_test_args(*args, **kwargs)
|
||||
cli_args.append('test_id')
|
||||
cli_args.append('test_policy')
|
||||
self._test_update_resource(resource, cmd, 'test_id',
|
||||
cli_args, api_args,
|
||||
cmd_resource=cmd_resource,
|
||||
parent_id='test_policy')
|
||||
|
||||
def test_create_rule_with_mandatory_params(self):
|
||||
# lbaas-l7rule-create with mandatory params only.
|
||||
|
||||
self._test_create_rule('type', 'compare-type',
|
||||
'value')
|
||||
|
||||
def test_create_disabled_rule(self):
|
||||
# lbaas-l7rule-create disabled rule.
|
||||
|
||||
self._test_create_rule('type', 'compare-type',
|
||||
'value', 'admin-state-down')
|
||||
|
||||
def test_create_rule_with_all_params(self):
|
||||
# lbaas-l7rule-create with all params set.
|
||||
|
||||
self._test_create_rule('type', 'compare-type',
|
||||
'invert-compare', 'key', 'value',
|
||||
type='HEADER', compare_type='CONTAINS',
|
||||
key='other_key', value='other_value')
|
||||
|
||||
def test_create_rule_with_inverted_compare(self):
|
||||
# lbaas-l7rule-create with invertted compare type.
|
||||
|
||||
self._test_create_rule('type', 'compare-type',
|
||||
'invert-compare', 'value')
|
||||
|
||||
def test_list_rules(self):
|
||||
# lbaas-l7rule-list.
|
||||
|
||||
resources = 'rules'
|
||||
cmd_resources = 'lbaas_l7rules'
|
||||
cmd = l7rule.ListL7Rule(test_cli20.MyApp(sys.stdout), None)
|
||||
|
||||
policy_id = 'policy_id'
|
||||
self._test_list_resources(resources, cmd, True,
|
||||
base_args=[policy_id],
|
||||
cmd_resources=cmd_resources,
|
||||
parent_id=policy_id,
|
||||
query="l7policy_id=%s" % policy_id)
|
||||
|
||||
def test_list_rules_pagination(self):
|
||||
# lbaas-l7rule-list with pagination.
|
||||
|
||||
resources = 'rules'
|
||||
cmd_resources = 'lbaas_l7rules'
|
||||
cmd = l7rule.ListL7Rule(test_cli20.MyApp(sys.stdout), None)
|
||||
policy_id = 'policy_id'
|
||||
self._test_list_resources_with_pagination(
|
||||
resources, cmd, base_args=[policy_id],
|
||||
cmd_resources=cmd_resources, parent_id=policy_id,
|
||||
query="l7policy_id=%s" % policy_id)
|
||||
|
||||
def test_list_rules_sort(self):
|
||||
# lbaas-l7rule-list --sort-key id --sort-key asc.
|
||||
|
||||
resources = 'rules'
|
||||
cmd_resources = 'lbaas_l7rules'
|
||||
cmd = l7rule.ListL7Rule(test_cli20.MyApp(sys.stdout), None)
|
||||
policy_id = 'policy_id'
|
||||
self._test_list_resources(
|
||||
resources, cmd, True, base_args=[policy_id],
|
||||
cmd_resources=cmd_resources, parent_id=policy_id,
|
||||
query="l7policy_id=%s" % policy_id)
|
||||
|
||||
def test_list_rules_limit(self):
|
||||
# lbaas-l7rule-list -P.
|
||||
|
||||
resources = 'rules'
|
||||
cmd_resources = 'lbaas_l7rules'
|
||||
cmd = l7rule.ListL7Rule(test_cli20.MyApp(sys.stdout), None)
|
||||
policy_id = 'policy_id'
|
||||
self._test_list_resources(resources, cmd, page_size=1000,
|
||||
base_args=[policy_id],
|
||||
cmd_resources=cmd_resources,
|
||||
parent_id=policy_id,
|
||||
query="l7policy_id=%s" % policy_id)
|
||||
|
||||
def test_show_rule_id(self):
|
||||
# lbaas-l7rule-show test_id.
|
||||
|
||||
resource = 'rule'
|
||||
cmd_resource = 'lbaas_l7rule'
|
||||
policy_id = 'policy_id'
|
||||
cmd = l7rule.ShowL7Rule(test_cli20.MyApp(sys.stdout), None)
|
||||
args = ['--fields', 'id', self.test_id, policy_id]
|
||||
self._test_show_resource(resource, cmd, self.test_id, args, ['id'],
|
||||
cmd_resource=cmd_resource,
|
||||
parent_id=policy_id)
|
||||
|
||||
def test_update_rule_type(self):
|
||||
# lbaas-l7rule-update test_id --type HEADER test_policy
|
||||
|
||||
self._test_update_rule('type', type='HEADER')
|
||||
|
||||
def test_update_rule_compare_type(self):
|
||||
# lbaas-l7rule-update test_id --compare-type CONTAINS test_policy.
|
||||
|
||||
self._test_update_rule('compare-type',
|
||||
**{'compare-type': 'CONTAINS'})
|
||||
|
||||
def test_update_rule_inverted_compare_type(self):
|
||||
# lbaas-l7rule-update test_id --invert-compare test_policy.
|
||||
|
||||
self._test_update_rule('invert-compare')
|
||||
|
||||
def test_update_rule_key_value(self):
|
||||
# lbaas-l7rule-update test_id --key other --value other test_policy.
|
||||
|
||||
self._test_update_rule('key', 'value',
|
||||
key='other', value='other')
|
||||
|
||||
def test_delete_rule(self):
|
||||
# lbaas-l7rule-delete test_id policy_id.
|
||||
|
||||
resource = 'rule'
|
||||
cmd_resource = 'lbaas_l7rule'
|
||||
policy_id = 'policy_id'
|
||||
test_id = 'test_id'
|
||||
cmd = l7rule.DeleteL7Rule(test_cli20.MyApp(sys.stdout), None)
|
||||
args = [test_id, policy_id]
|
||||
self._test_delete_resource(resource, cmd, test_id, args,
|
||||
cmd_resource=cmd_resource,
|
||||
parent_id=policy_id)
|
||||
@@ -16,14 +16,15 @@
|
||||
|
||||
import sys
|
||||
|
||||
from neutronclient.common import exceptions
|
||||
from neutronclient.neutron.v2_0.lb.v2 import listener
|
||||
from neutronclient.tests.unit import test_cli20
|
||||
|
||||
|
||||
class CLITestV20LbListenerJSON(test_cli20.CLITestV20Base):
|
||||
|
||||
def test_create_listener_with_mandatory_params(self):
|
||||
# lbaas-listener-create with mandatory params only.
|
||||
def test_create_listener_with_loadbalancer(self):
|
||||
# lbaas-listener-create with --loadbalancer
|
||||
resource = 'listener'
|
||||
cmd_resource = 'lbaas_listener'
|
||||
cmd = listener.CreateListener(test_cli20.MyApp(sys.stdout), None)
|
||||
@@ -40,6 +41,41 @@ class CLITestV20LbListenerJSON(test_cli20.CLITestV20Base):
|
||||
position_names, position_values,
|
||||
cmd_resource=cmd_resource)
|
||||
|
||||
def test_create_listener_with_default_pool(self):
|
||||
# lbaas-listener-create with --default-pool and no --loadbalancer.
|
||||
resource = 'listener'
|
||||
cmd_resource = 'lbaas_listener'
|
||||
cmd = listener.CreateListener(test_cli20.MyApp(sys.stdout), None)
|
||||
my_id = 'my-id'
|
||||
default_pool_id = 'default-pool'
|
||||
protocol = 'TCP'
|
||||
protocol_port = '80'
|
||||
args = ['--protocol', protocol, '--protocol-port', protocol_port,
|
||||
'--default-pool', default_pool_id]
|
||||
position_names = ['protocol', 'protocol_port', 'default_pool_id']
|
||||
position_values = [protocol, protocol_port, default_pool_id,
|
||||
True]
|
||||
self._test_create_resource(resource, cmd, '', my_id, args,
|
||||
position_names, position_values,
|
||||
cmd_resource=cmd_resource)
|
||||
|
||||
def test_create_listener_with_no_loadbalancer_or_default_pool(self):
|
||||
# lbaas-listener-create without --default-pool or --loadbalancer.
|
||||
resource = 'listener'
|
||||
cmd_resource = 'lbaas_listener'
|
||||
cmd = listener.CreateListener(test_cli20.MyApp(sys.stdout), None)
|
||||
my_id = 'my-id'
|
||||
protocol = 'TCP'
|
||||
protocol_port = '80'
|
||||
args = ['--protocol', protocol, '--protocol-port', protocol_port]
|
||||
position_names = ['protocol', 'protocol_port']
|
||||
position_values = [protocol, protocol_port, True]
|
||||
self._test_create_resource(resource, cmd, '', my_id, args,
|
||||
position_names, position_values,
|
||||
cmd_resource=cmd_resource,
|
||||
no_api_call=True,
|
||||
expected_exception=exceptions.CommandError)
|
||||
|
||||
def test_create_listener_with_all_params(self):
|
||||
# lbaas-listener-create with all params set.
|
||||
resource = 'listener'
|
||||
@@ -47,6 +83,7 @@ class CLITestV20LbListenerJSON(test_cli20.CLITestV20Base):
|
||||
cmd = listener.CreateListener(test_cli20.MyApp(sys.stdout), None)
|
||||
my_id = 'my-id'
|
||||
loadbalancer = 'loadbalancer'
|
||||
default_pool_id = 'default-pool'
|
||||
protocol = 'TCP'
|
||||
protocol_port = '80'
|
||||
connection_limit = 10
|
||||
@@ -54,14 +91,17 @@ class CLITestV20LbListenerJSON(test_cli20.CLITestV20Base):
|
||||
args = ['--admin-state-down',
|
||||
'--protocol', protocol, '--protocol-port', protocol_port,
|
||||
'--loadbalancer', loadbalancer,
|
||||
'--default-pool', default_pool_id,
|
||||
'--default-tls-container-ref', def_tls_cont_ref,
|
||||
'--sni-container-refs', '1111', '2222', '3333',
|
||||
'--connection-limit', '10']
|
||||
position_names = ['admin_state_up',
|
||||
'protocol', 'protocol_port', 'loadbalancer_id',
|
||||
'default_pool_id',
|
||||
'default_tls_container_ref', 'sni_container_refs',
|
||||
'connection_limit']
|
||||
position_values = [False, protocol, protocol_port, loadbalancer,
|
||||
default_pool_id,
|
||||
def_tls_cont_ref, ['1111', '2222', '3333'],
|
||||
connection_limit]
|
||||
self._test_create_resource(resource, cmd, '', my_id, args,
|
||||
|
||||
@@ -166,3 +166,40 @@ class CLITestV20LbLoadBalancerJSON(test_cli20.CLITestV20Base):
|
||||
self.assertIn('1234', _str)
|
||||
self.assertIn('bytes_out', _str)
|
||||
self.assertIn('4321', _str)
|
||||
|
||||
def test_get_loadbalancer_statuses(self):
|
||||
# lbaas-loadbalancer-status test_id.
|
||||
resource = 'loadbalancer'
|
||||
cmd = lb.RetrieveLoadBalancerStatus(test_cli20.MyApp(sys.stdout), None)
|
||||
my_id = self.test_id
|
||||
args = [my_id]
|
||||
|
||||
self.mox.StubOutWithMock(cmd, "get_client")
|
||||
self.mox.StubOutWithMock(self.client.httpclient, "request")
|
||||
cmd.get_client().MultipleTimes().AndReturn(self.client)
|
||||
|
||||
expected_res = {'statuses': {'operating_status': 'ONLINE',
|
||||
'provisioning_status': 'ACTIVE'}}
|
||||
|
||||
resstr = self.client.serialize(expected_res)
|
||||
|
||||
path = getattr(self.client, "lbaas_loadbalancer_path_status")
|
||||
return_tup = (test_cli20.MyResp(200), resstr)
|
||||
self.client.httpclient.request(
|
||||
test_cli20.end_url(path % my_id), '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.assertIn('operating_status', _str)
|
||||
self.assertIn('ONLINE', _str)
|
||||
self.assertIn('provisioning_status', _str)
|
||||
self.assertIn('ACTIVE', _str)
|
||||
|
||||
@@ -16,14 +16,15 @@
|
||||
|
||||
import sys
|
||||
|
||||
from neutronclient.common import exceptions
|
||||
from neutronclient.neutron.v2_0.lb.v2 import pool
|
||||
from neutronclient.tests.unit import test_cli20
|
||||
|
||||
|
||||
class CLITestV20LbPoolJSON(test_cli20.CLITestV20Base):
|
||||
|
||||
def test_create_pool_with_mandatory_params(self):
|
||||
# lbaas-pool-create with mandatory params only.
|
||||
def test_create_pool_with_listener(self):
|
||||
# lbaas-pool-create with listener
|
||||
resource = 'pool'
|
||||
cmd_resource = 'lbaas_pool'
|
||||
cmd = pool.CreatePool(test_cli20.MyApp(sys.stdout), None)
|
||||
@@ -40,6 +41,41 @@ class CLITestV20LbPoolJSON(test_cli20.CLITestV20Base):
|
||||
position_names, position_values,
|
||||
cmd_resource=cmd_resource)
|
||||
|
||||
def test_create_pool_with_loadbalancer_no_listener(self):
|
||||
"""lbaas-pool-create with loadbalancer, no listener."""
|
||||
resource = 'pool'
|
||||
cmd_resource = 'lbaas_pool'
|
||||
cmd = pool.CreatePool(test_cli20.MyApp(sys.stdout), None)
|
||||
my_id = 'my-id'
|
||||
lb_algorithm = 'ROUND_ROBIN'
|
||||
loadbalancer = 'loadbalancer'
|
||||
protocol = 'TCP'
|
||||
args = ['--lb-algorithm', lb_algorithm, '--protocol', protocol,
|
||||
'--loadbalancer', loadbalancer]
|
||||
position_names = ['admin_state_up', 'lb_algorithm', 'protocol',
|
||||
'loadbalancer_id']
|
||||
position_values = [True, lb_algorithm, protocol, loadbalancer]
|
||||
self._test_create_resource(resource, cmd, '', my_id, args,
|
||||
position_names, position_values,
|
||||
cmd_resource=cmd_resource)
|
||||
|
||||
def test_create_pool_with_no_listener_or_loadbalancer(self):
|
||||
"""lbaas-pool-create with no listener or loadbalancer."""
|
||||
resource = 'pool'
|
||||
cmd_resource = 'lbaas_pool'
|
||||
cmd = pool.CreatePool(test_cli20.MyApp(sys.stdout), None)
|
||||
my_id = 'my-id'
|
||||
lb_algorithm = 'ROUND_ROBIN'
|
||||
protocol = 'TCP'
|
||||
args = ['--lb-algorithm', lb_algorithm, '--protocol', protocol]
|
||||
position_names = ['admin_state_up', 'lb_algorithm', 'protocol']
|
||||
position_values = [True, lb_algorithm, protocol]
|
||||
self._test_create_resource(resource, cmd, '', my_id, args,
|
||||
position_names, position_values,
|
||||
cmd_resource=cmd_resource,
|
||||
no_api_call=True,
|
||||
expected_exception=exceptions.CommandError)
|
||||
|
||||
def test_create_pool_with_all_params(self):
|
||||
# lbaas-pool-create with all params set.
|
||||
resource = 'pool'
|
||||
@@ -48,6 +84,7 @@ class CLITestV20LbPoolJSON(test_cli20.CLITestV20Base):
|
||||
my_id = 'my-id'
|
||||
lb_algorithm = 'ROUND_ROBIN'
|
||||
listener = 'listener'
|
||||
loadbalancer = 'loadbalancer'
|
||||
protocol = 'TCP'
|
||||
description = 'description'
|
||||
session_persistence_str = 'type=APP_COOKIE,cookie_name=1234'
|
||||
@@ -57,12 +94,13 @@ class CLITestV20LbPoolJSON(test_cli20.CLITestV20Base):
|
||||
args = ['--lb-algorithm', lb_algorithm, '--protocol', protocol,
|
||||
'--description', description, '--session-persistence',
|
||||
session_persistence_str, '--admin-state-down', '--name', name,
|
||||
'--listener', listener]
|
||||
'--listener', listener, '--loadbalancer', loadbalancer]
|
||||
position_names = ['lb_algorithm', 'protocol', 'description',
|
||||
'session_persistence', 'admin_state_up', 'name',
|
||||
'listener_id']
|
||||
'listener_id', 'loadbalancer_id']
|
||||
position_values = [lb_algorithm, protocol, description,
|
||||
session_persistence, False, name, listener]
|
||||
session_persistence, False, name, listener,
|
||||
loadbalancer]
|
||||
self._test_create_resource(resource, cmd, '', my_id, args,
|
||||
position_names, position_values,
|
||||
cmd_resource=cmd_resource)
|
||||
|
||||
55
neutronclient/tests/unit/test_auto_allocated_topology.py
Executable file
55
neutronclient/tests/unit/test_auto_allocated_topology.py
Executable file
@@ -0,0 +1,55 @@
|
||||
# Copyright 2016 IBM
|
||||
# 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 neutronclient.neutron.v2_0 import auto_allocated_topology as aat
|
||||
from neutronclient.tests.unit import test_cli20
|
||||
|
||||
|
||||
class TestAutoAllocatedTopologyJSON(test_cli20.CLITestV20Base):
|
||||
|
||||
def test_show_auto_allocated_topology_arg(self):
|
||||
resource = 'auto_allocated_topology'
|
||||
cmd = aat.ShowAutoAllocatedTopology(test_cli20.MyApp(sys.stdout), None)
|
||||
args = ['--tenant-id', self.test_id]
|
||||
self._test_show_resource(resource, cmd, self.test_id, args)
|
||||
|
||||
def test_show_auto_allocated_topology_posarg(self):
|
||||
resource = 'auto_allocated_topology'
|
||||
cmd = aat.ShowAutoAllocatedTopology(test_cli20.MyApp(sys.stdout), None)
|
||||
args = ['some-tenant']
|
||||
self._test_show_resource(resource, cmd, "some-tenant", args)
|
||||
|
||||
def test_show_auto_allocated_topology_no_arg(self):
|
||||
resource = 'auto_allocated_topology'
|
||||
cmd = aat.ShowAutoAllocatedTopology(test_cli20.MyApp(sys.stdout), None)
|
||||
args = []
|
||||
self._test_show_resource(resource, cmd, "None", args)
|
||||
|
||||
def test_show_auto_allocated_topology_dry_run_as_tenant(self):
|
||||
resource = 'auto_allocated_topology'
|
||||
cmd = aat.ShowAutoAllocatedTopology(test_cli20.MyApp(sys.stdout), None)
|
||||
args = ['--dry-run']
|
||||
self._test_show_resource(resource, cmd, "None", args,
|
||||
fields=('dry-run',))
|
||||
|
||||
def test_show_auto_allocated_topology_dry_run_as_admin(self):
|
||||
resource = 'auto_allocated_topology'
|
||||
cmd = aat.ShowAutoAllocatedTopology(test_cli20.MyApp(sys.stdout), None)
|
||||
args = ['--dry-run', 'some-tenant']
|
||||
self._test_show_resource(resource, cmd, "some-tenant", args,
|
||||
fields=('dry-run',))
|
||||
@@ -37,6 +37,7 @@ API_VERSION = "2.0"
|
||||
FORMAT = 'json'
|
||||
TOKEN = 'testtoken'
|
||||
ENDURL = 'localurl'
|
||||
REQUEST_ID = 'test_request_id'
|
||||
|
||||
|
||||
@contextlib.contextmanager
|
||||
@@ -65,7 +66,7 @@ class FakeStdout(object):
|
||||
return result
|
||||
|
||||
|
||||
class MyResp(object):
|
||||
class MyResp(requests.Response):
|
||||
def __init__(self, status_code, headers=None, reason=None):
|
||||
self.status_code = status_code
|
||||
self.headers = headers or {}
|
||||
@@ -557,6 +558,81 @@ class CLITestV20Base(base.BaseTestCase):
|
||||
self.assertIn(myid, _str)
|
||||
|
||||
|
||||
class TestListCommand(neutronV2_0.ListCommand):
|
||||
resource = 'test_resource'
|
||||
filter_attrs = [
|
||||
'name',
|
||||
'admin_state_up',
|
||||
{'name': 'foo', 'help': 'non-boolean attribute foo'},
|
||||
{'name': 'bar', 'help': 'boolean attribute bar',
|
||||
'boolean': True},
|
||||
{'name': 'baz', 'help': 'integer attribute baz',
|
||||
'argparse_kwargs': {'choices': ['baz1', 'baz2']}},
|
||||
]
|
||||
|
||||
|
||||
class ListCommandTestCase(CLITestV20Base):
|
||||
|
||||
def setUp(self):
|
||||
super(ListCommandTestCase, self).setUp()
|
||||
self.client.extend_list('test_resources', '/test_resources', None)
|
||||
setattr(self.client, 'test_resources_path', '/test_resources')
|
||||
|
||||
def _test_list_resources_filter_params(self, base_args='', query=''):
|
||||
resources = 'test_resources'
|
||||
cmd = TestListCommand(MyApp(sys.stdout), None)
|
||||
self._test_list_resources(resources, cmd,
|
||||
base_args=base_args.split(),
|
||||
query=query)
|
||||
|
||||
def _test_list_resources_with_arg_error(self, base_args=''):
|
||||
self.addCleanup(self.mox.UnsetStubs)
|
||||
resources = 'test_resources'
|
||||
cmd = TestListCommand(MyApp(sys.stdout), None)
|
||||
# argparse parse error leads to SystemExit
|
||||
self.assertRaises(SystemExit,
|
||||
self._test_list_resources,
|
||||
resources, cmd,
|
||||
base_args=base_args.split())
|
||||
|
||||
def test_list_resources_without_filter(self):
|
||||
self._test_list_resources_filter_params()
|
||||
|
||||
def test_list_resources_use_default_filter(self):
|
||||
self._test_list_resources_filter_params(
|
||||
base_args='--name val1 --admin-state-up False',
|
||||
query='name=val1&admin_state_up=False')
|
||||
|
||||
def test_list_resources_use_custom_filter(self):
|
||||
self._test_list_resources_filter_params(
|
||||
base_args='--foo FOO --bar True',
|
||||
query='foo=FOO&bar=True')
|
||||
|
||||
def test_list_resources_boolean_check_default_filter(self):
|
||||
self._test_list_resources_filter_params(
|
||||
base_args='--admin-state-up True', query='admin_state_up=True')
|
||||
self._test_list_resources_filter_params(
|
||||
base_args='--admin-state-up False', query='admin_state_up=False')
|
||||
self._test_list_resources_with_arg_error(
|
||||
base_args='--admin-state-up non-true-false')
|
||||
|
||||
def test_list_resources_boolean_check_custom_filter(self):
|
||||
self._test_list_resources_filter_params(
|
||||
base_args='--bar True', query='bar=True')
|
||||
self._test_list_resources_filter_params(
|
||||
base_args='--bar False', query='bar=False')
|
||||
self._test_list_resources_with_arg_error(
|
||||
base_args='--bar non-true-false')
|
||||
|
||||
def test_list_resources_argparse_kwargs(self):
|
||||
self._test_list_resources_filter_params(
|
||||
base_args='--baz baz1', query='baz=baz1')
|
||||
self._test_list_resources_filter_params(
|
||||
base_args='--baz baz2', query='baz=baz2')
|
||||
self._test_list_resources_with_arg_error(
|
||||
base_args='--bar non-choice')
|
||||
|
||||
|
||||
class ClientV2TestJson(CLITestV20Base):
|
||||
def test_do_request_unicode(self):
|
||||
self.mox.StubOutWithMock(self.client.httpclient, "request")
|
||||
@@ -573,41 +649,46 @@ class ClientV2TestJson(CLITestV20Base):
|
||||
self.client.httpclient.auth_token = encodeutils.safe_encode(
|
||||
unicode_text)
|
||||
expected_auth_token = encodeutils.safe_encode(unicode_text)
|
||||
resp_headers = {'x-openstack-request-id': REQUEST_ID}
|
||||
|
||||
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))
|
||||
expected_auth_token)).AndReturn((MyResp(200, resp_headers),
|
||||
expect_body))
|
||||
|
||||
self.mox.ReplayAll()
|
||||
res_body = self.client.do_request('PUT', action, body=body,
|
||||
params=params)
|
||||
result = self.client.do_request('PUT', action, body=body,
|
||||
params=params)
|
||||
self.mox.VerifyAll()
|
||||
self.mox.UnsetStubs()
|
||||
|
||||
# test response with unicode
|
||||
self.assertEqual(body, res_body)
|
||||
self.assertEqual(body, result)
|
||||
|
||||
def test_do_request_error_without_response_body(self):
|
||||
self.mox.StubOutWithMock(self.client.httpclient, "request")
|
||||
params = {'test': 'value'}
|
||||
expect_query = six.moves.urllib.parse.urlencode(params)
|
||||
self.client.httpclient.auth_token = 'token'
|
||||
resp_headers = {'x-openstack-request-id': REQUEST_ID}
|
||||
|
||||
self.client.httpclient.request(
|
||||
MyUrlComparator(end_url(
|
||||
'/test', query=expect_query, format=self.format), self.client),
|
||||
'PUT', body='',
|
||||
headers=mox.ContainsKeyValue('X-Auth-Token', 'token')
|
||||
).AndReturn((MyResp(400, reason='An error'), ''))
|
||||
).AndReturn((MyResp(400, headers=resp_headers, reason='An error'), ''))
|
||||
|
||||
self.mox.ReplayAll()
|
||||
error = self.assertRaises(exceptions.NeutronClientException,
|
||||
self.client.do_request, 'PUT', '/test',
|
||||
body='', params=params)
|
||||
self.assertEqual("An error", str(error))
|
||||
expected_error = "An error\nNeutron server returns " \
|
||||
"request_ids: %s" % [REQUEST_ID]
|
||||
self.assertEqual(expected_error, str(error))
|
||||
self.mox.VerifyAll()
|
||||
self.mox.UnsetStubs()
|
||||
|
||||
@@ -622,22 +703,129 @@ class ClientV2TestJson(CLITestV20Base):
|
||||
else:
|
||||
self.fail('Expected exception NOT raised')
|
||||
|
||||
def test_do_request_request_ids(self):
|
||||
self.mox.StubOutWithMock(self.client.httpclient, "request")
|
||||
params = {'test': 'value'}
|
||||
expect_query = six.moves.urllib.parse.urlencode(params)
|
||||
self.client.httpclient.auth_token = 'token'
|
||||
body = params
|
||||
expect_body = self.client.serialize(body)
|
||||
resp_headers = {'x-openstack-request-id': REQUEST_ID}
|
||||
self.client.httpclient.request(
|
||||
MyUrlComparator(end_url(
|
||||
'/test', query=expect_query,
|
||||
format=self.format), self.client),
|
||||
'PUT', body=expect_body,
|
||||
headers=mox.ContainsKeyValue('X-Auth-Token', 'token')
|
||||
).AndReturn((MyResp(200, resp_headers), expect_body))
|
||||
|
||||
self.mox.ReplayAll()
|
||||
result = self.client.do_request('PUT', '/test', body=body,
|
||||
params=params)
|
||||
self.mox.VerifyAll()
|
||||
self.mox.UnsetStubs()
|
||||
|
||||
self.assertEqual(body, result)
|
||||
self.assertEqual([REQUEST_ID], result.request_ids)
|
||||
|
||||
def test_list_request_ids_with_retrieve_all_true(self):
|
||||
self.mox.StubOutWithMock(self.client.httpclient, "request")
|
||||
|
||||
path = '/test'
|
||||
resources = 'tests'
|
||||
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', }]}
|
||||
resstr1 = self.client.serialize(reses1)
|
||||
resstr2 = self.client.serialize(reses2)
|
||||
resp_headers = {'x-openstack-request-id': REQUEST_ID}
|
||||
self.client.httpclient.request(
|
||||
end_url(path, "", format=self.format), 'GET',
|
||||
body=None,
|
||||
headers=mox.ContainsKeyValue(
|
||||
'X-Auth-Token', TOKEN)).AndReturn((MyResp(200, resp_headers),
|
||||
resstr1))
|
||||
self.client.httpclient.request(
|
||||
MyUrlComparator(end_url(path, fake_query, format=self.format),
|
||||
self.client), 'GET',
|
||||
body=None,
|
||||
headers=mox.ContainsKeyValue(
|
||||
'X-Auth-Token', TOKEN)).AndReturn((MyResp(200, resp_headers),
|
||||
resstr2))
|
||||
self.mox.ReplayAll()
|
||||
result = self.client.list(resources, path)
|
||||
|
||||
self.mox.VerifyAll()
|
||||
self.mox.UnsetStubs()
|
||||
|
||||
self.assertEqual([REQUEST_ID, REQUEST_ID], result.request_ids)
|
||||
|
||||
def test_list_request_ids_with_retrieve_all_false(self):
|
||||
self.mox.StubOutWithMock(self.client.httpclient, "request")
|
||||
|
||||
path = '/test'
|
||||
resources = 'tests'
|
||||
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', }]}
|
||||
resstr1 = self.client.serialize(reses1)
|
||||
resstr2 = self.client.serialize(reses2)
|
||||
resp_headers = {'x-openstack-request-id': REQUEST_ID}
|
||||
self.client.httpclient.request(
|
||||
end_url(path, "", format=self.format), 'GET',
|
||||
body=None,
|
||||
headers=mox.ContainsKeyValue(
|
||||
'X-Auth-Token', TOKEN)).AndReturn((MyResp(200, resp_headers),
|
||||
resstr1))
|
||||
self.client.httpclient.request(
|
||||
MyUrlComparator(end_url(path, fake_query, format=self.format),
|
||||
self.client), 'GET',
|
||||
body=None,
|
||||
headers=mox.ContainsKeyValue(
|
||||
'X-Auth-Token', TOKEN)).AndReturn((MyResp(200, resp_headers),
|
||||
resstr2))
|
||||
self.mox.ReplayAll()
|
||||
result = self.client.list(resources, path, retrieve_all=False)
|
||||
next(result)
|
||||
self.assertEqual([REQUEST_ID], result.request_ids)
|
||||
next(result)
|
||||
self.assertEqual([REQUEST_ID, REQUEST_ID], result.request_ids)
|
||||
self.mox.VerifyAll()
|
||||
self.mox.UnsetStubs()
|
||||
|
||||
|
||||
class CLITestV20ExceptionHandler(CLITestV20Base):
|
||||
|
||||
def _test_exception_handler_v20(
|
||||
self, expected_exception, status_code, expected_msg,
|
||||
error_type=None, error_msg=None, error_detail=None,
|
||||
error_content=None):
|
||||
request_id=None, error_content=None):
|
||||
|
||||
resp = MyResp(status_code, {'x-openstack-request-id': request_id})
|
||||
if request_id is not None:
|
||||
expected_msg += "\nNeutron server returns " \
|
||||
"request_ids: %s" % [request_id]
|
||||
if error_content is None:
|
||||
error_content = {'NeutronError': {'type': error_type,
|
||||
'message': error_msg,
|
||||
'detail': error_detail}}
|
||||
expected_content = self.client._convert_into_with_meta(error_content,
|
||||
resp)
|
||||
|
||||
e = self.assertRaises(expected_exception,
|
||||
client.exception_handler_v20,
|
||||
status_code, error_content)
|
||||
status_code, expected_content)
|
||||
self.assertEqual(status_code, e.status_code)
|
||||
self.assertEqual(expected_exception.__name__,
|
||||
e.__class__.__name__)
|
||||
|
||||
if expected_msg is None:
|
||||
if error_detail:
|
||||
@@ -651,7 +839,7 @@ class CLITestV20ExceptionHandler(CLITestV20Base):
|
||||
'fake-network-uuid. The IP address fake-ip is in use.')
|
||||
self._test_exception_handler_v20(
|
||||
exceptions.IpAddressInUseClient, 409, err_msg,
|
||||
'IpAddressInUse', err_msg, '')
|
||||
'IpAddressInUse', err_msg, '', REQUEST_ID)
|
||||
|
||||
def test_exception_handler_v20_neutron_known_error(self):
|
||||
known_error_map = [
|
||||
@@ -677,7 +865,7 @@ class CLITestV20ExceptionHandler(CLITestV20Base):
|
||||
self._test_exception_handler_v20(
|
||||
client_exc, status_code,
|
||||
error_msg + '\n' + error_detail,
|
||||
server_exc, error_msg, error_detail)
|
||||
server_exc, error_msg, error_detail, REQUEST_ID)
|
||||
|
||||
def test_exception_handler_v20_neutron_known_error_without_detail(self):
|
||||
error_msg = 'Network not found'
|
||||
@@ -685,7 +873,7 @@ class CLITestV20ExceptionHandler(CLITestV20Base):
|
||||
self._test_exception_handler_v20(
|
||||
exceptions.NetworkNotFoundClient, 404,
|
||||
error_msg,
|
||||
'NetworkNotFound', error_msg, error_detail)
|
||||
'NetworkNotFound', error_msg, error_detail, REQUEST_ID)
|
||||
|
||||
def test_exception_handler_v20_unknown_error_to_per_code_exception(self):
|
||||
for status_code, client_exc in exceptions.HTTP_EXCEPTION_MAP.items():
|
||||
@@ -694,7 +882,7 @@ class CLITestV20ExceptionHandler(CLITestV20Base):
|
||||
self._test_exception_handler_v20(
|
||||
client_exc, status_code,
|
||||
error_msg + '\n' + error_detail,
|
||||
'UnknownError', error_msg, error_detail)
|
||||
'UnknownError', error_msg, error_detail, [REQUEST_ID])
|
||||
|
||||
def test_exception_handler_v20_neutron_unknown_status_code(self):
|
||||
error_msg = 'Unknown error'
|
||||
@@ -702,36 +890,44 @@ class CLITestV20ExceptionHandler(CLITestV20Base):
|
||||
self._test_exception_handler_v20(
|
||||
exceptions.NeutronClientException, 501,
|
||||
error_msg + '\n' + error_detail,
|
||||
'UnknownError', error_msg, error_detail)
|
||||
'UnknownError', error_msg, error_detail, REQUEST_ID)
|
||||
|
||||
def test_exception_handler_v20_bad_neutron_error(self):
|
||||
error_content = {'NeutronError': {'unknown_key': 'UNKNOWN'}}
|
||||
self._test_exception_handler_v20(
|
||||
exceptions.NeutronClientException, 500,
|
||||
expected_msg={'unknown_key': 'UNKNOWN'},
|
||||
error_content=error_content)
|
||||
for status_code, client_exc in exceptions.HTTP_EXCEPTION_MAP.items():
|
||||
error_content = {'NeutronError': {'unknown_key': 'UNKNOWN'}}
|
||||
self._test_exception_handler_v20(
|
||||
client_exc, status_code,
|
||||
expected_msg="{'unknown_key': 'UNKNOWN'}",
|
||||
error_content=error_content,
|
||||
request_id=REQUEST_ID)
|
||||
|
||||
def test_exception_handler_v20_error_dict_contains_message(self):
|
||||
error_content = {'message': 'This is an error message'}
|
||||
self._test_exception_handler_v20(
|
||||
exceptions.NeutronClientException, 500,
|
||||
expected_msg='This is an error message',
|
||||
error_content=error_content)
|
||||
for status_code, client_exc in exceptions.HTTP_EXCEPTION_MAP.items():
|
||||
self._test_exception_handler_v20(
|
||||
client_exc, status_code,
|
||||
expected_msg='This is an error message',
|
||||
error_content=error_content,
|
||||
request_id=REQUEST_ID)
|
||||
|
||||
def test_exception_handler_v20_error_dict_not_contain_message(self):
|
||||
# 599 is not contained in HTTP_EXCEPTION_MAP.
|
||||
error_content = {'error': 'This is an error message'}
|
||||
expected_msg = '%s-%s' % (500, error_content)
|
||||
expected_msg = '%s-%s' % (599, error_content)
|
||||
self._test_exception_handler_v20(
|
||||
exceptions.NeutronClientException, 500,
|
||||
exceptions.NeutronClientException, 599,
|
||||
expected_msg=expected_msg,
|
||||
request_id=None,
|
||||
error_content=error_content)
|
||||
|
||||
def test_exception_handler_v20_default_fallback(self):
|
||||
# 599 is not contained in HTTP_EXCEPTION_MAP.
|
||||
error_content = 'This is an error message'
|
||||
expected_msg = '%s-%s' % (500, error_content)
|
||||
expected_msg = '%s-%s' % (599, error_content)
|
||||
self._test_exception_handler_v20(
|
||||
exceptions.NeutronClientException, 500,
|
||||
exceptions.NeutronClientException, 599,
|
||||
expected_msg=expected_msg,
|
||||
request_id=None,
|
||||
error_content=error_content)
|
||||
|
||||
def test_exception_status(self):
|
||||
@@ -767,3 +963,60 @@ class CLITestV20ExceptionHandler(CLITestV20Base):
|
||||
self.assertIsNotNone(error.status_code)
|
||||
self.mox.VerifyAll()
|
||||
self.mox.UnsetStubs()
|
||||
|
||||
|
||||
class DictWithMetaTest(base.BaseTestCase):
|
||||
|
||||
def test_dict_with_meta(self):
|
||||
body = {'test': 'value'}
|
||||
resp = MyResp(200, {'x-openstack-request-id': REQUEST_ID})
|
||||
obj = client._DictWithMeta(body, resp)
|
||||
self.assertEqual(body, obj)
|
||||
|
||||
# Check request_ids attribute is added to obj
|
||||
self.assertTrue(hasattr(obj, 'request_ids'))
|
||||
self.assertEqual([REQUEST_ID], obj.request_ids)
|
||||
|
||||
|
||||
class TupleWithMetaTest(base.BaseTestCase):
|
||||
|
||||
def test_tuple_with_meta(self):
|
||||
body = ('test', 'value')
|
||||
resp = MyResp(200, {'x-openstack-request-id': REQUEST_ID})
|
||||
obj = client._TupleWithMeta(body, resp)
|
||||
self.assertEqual(body, obj)
|
||||
|
||||
# Check request_ids attribute is added to obj
|
||||
self.assertTrue(hasattr(obj, 'request_ids'))
|
||||
self.assertEqual([REQUEST_ID], obj.request_ids)
|
||||
|
||||
|
||||
class StrWithMetaTest(base.BaseTestCase):
|
||||
|
||||
def test_str_with_meta(self):
|
||||
body = "test_string"
|
||||
resp = MyResp(200, {'x-openstack-request-id': REQUEST_ID})
|
||||
obj = client._StrWithMeta(body, resp)
|
||||
self.assertEqual(body, obj)
|
||||
|
||||
# Check request_ids attribute is added to obj
|
||||
self.assertTrue(hasattr(obj, 'request_ids'))
|
||||
self.assertEqual([REQUEST_ID], obj.request_ids)
|
||||
|
||||
|
||||
class GeneratorWithMetaTest(base.BaseTestCase):
|
||||
|
||||
body = {'test': 'value'}
|
||||
resp = MyResp(200, {'x-openstack-request-id': REQUEST_ID})
|
||||
|
||||
def _pagination(self, collection, path, **params):
|
||||
obj = client._DictWithMeta(self.body, self.resp)
|
||||
yield obj
|
||||
|
||||
def test_generator(self):
|
||||
obj = client._GeneratorWithMeta(self._pagination, 'test_collection',
|
||||
'test_path', test_args='test_args')
|
||||
self.assertEqual(self.body, next(obj))
|
||||
|
||||
self.assertTrue(hasattr(obj, 'request_ids'))
|
||||
self.assertEqual([REQUEST_ID], obj.request_ids)
|
||||
|
||||
@@ -30,19 +30,46 @@ class CLITestV20AddressScopeJSON(test_cli20.CLITestV20Base):
|
||||
def setUp(self):
|
||||
super(CLITestV20AddressScopeJSON, self).setUp(plurals={'tags': 'tag'})
|
||||
|
||||
def test_create_address_scope_with_minimum_option(self):
|
||||
# Create address_scope: foo-address-scope with minimum option.
|
||||
def test_create_address_scope_with_minimum_option_ipv4(self):
|
||||
"""Create address_scope: foo-address-scope with minimum option."""
|
||||
resource = 'address_scope'
|
||||
cmd = address_scope.CreateAddressScope(
|
||||
test_cli20.MyApp(sys.stdout), None)
|
||||
name = 'foo-address-scope'
|
||||
myid = 'myid'
|
||||
args = [name]
|
||||
position_names = ['name']
|
||||
position_values = [name]
|
||||
args = [name, '4']
|
||||
position_names = ['name', 'ip_version']
|
||||
position_values = [name, 4]
|
||||
self._test_create_resource(resource, cmd, name, myid, args,
|
||||
position_names, position_values)
|
||||
|
||||
def test_create_address_scope_with_minimum_option_ipv6(self):
|
||||
"""Create address_scope: foo-address-scope with minimum option."""
|
||||
resource = 'address_scope'
|
||||
cmd = address_scope.CreateAddressScope(
|
||||
test_cli20.MyApp(sys.stdout), None)
|
||||
name = 'foo-address-scope'
|
||||
myid = 'myid'
|
||||
args = [name, '6']
|
||||
position_names = ['name', 'ip_version']
|
||||
position_values = [name, 6]
|
||||
self._test_create_resource(resource, cmd, name, myid, args,
|
||||
position_names, position_values)
|
||||
|
||||
def test_create_address_scope_with_minimum_option_bad_ip_version(self):
|
||||
"""Create address_scope: foo-address-scope with minimum option."""
|
||||
resource = 'address_scope'
|
||||
cmd = address_scope.CreateAddressScope(
|
||||
test_cli20.MyApp(sys.stdout), None)
|
||||
name = 'foo-address-scope'
|
||||
myid = 'myid'
|
||||
args = [name, '5']
|
||||
position_names = ['name', 'ip_version']
|
||||
position_values = [name, 5]
|
||||
self.assertRaises(SystemExit, self._test_create_resource,
|
||||
resource, cmd, name, myid, args, position_names,
|
||||
position_values)
|
||||
|
||||
def test_create_address_scope_with_all_option(self):
|
||||
# Create address_scope: foo-address-scope with all options.
|
||||
resource = 'address_scope'
|
||||
@@ -50,9 +77,9 @@ class CLITestV20AddressScopeJSON(test_cli20.CLITestV20Base):
|
||||
test_cli20.MyApp(sys.stdout), None)
|
||||
name = 'foo-address-scope'
|
||||
myid = 'myid'
|
||||
args = [name, '--shared']
|
||||
position_names = ['name', 'shared']
|
||||
position_values = [name, True]
|
||||
args = [name, '4', '--shared']
|
||||
position_names = ['name', 'ip_version', 'shared']
|
||||
position_values = [name, 4, True]
|
||||
self._test_create_resource(resource, cmd, name, myid, args,
|
||||
position_names, position_values)
|
||||
|
||||
@@ -62,10 +89,11 @@ class CLITestV20AddressScopeJSON(test_cli20.CLITestV20Base):
|
||||
cmd = address_scope.CreateAddressScope(
|
||||
test_cli20.MyApp(sys.stdout), None)
|
||||
name = u'\u7f51\u7edc'
|
||||
ip_version = u'4'
|
||||
myid = 'myid'
|
||||
args = [name]
|
||||
position_names = ['name']
|
||||
position_values = [name]
|
||||
args = [name, ip_version]
|
||||
position_names = ['name', 'ip_version']
|
||||
position_values = [name, 4]
|
||||
self._test_create_resource(resource, cmd, name, myid, args,
|
||||
position_names, position_values)
|
||||
|
||||
@@ -115,7 +143,7 @@ class CLITestV20AddressScopeJSON(test_cli20.CLITestV20Base):
|
||||
cmd = address_scope.ListAddressScope(test_cli20.MyApp(sys.stdout),
|
||||
None)
|
||||
self._test_list_resources(resources, cmd,
|
||||
sort_key=["name", "id"],
|
||||
sort_key=["name", "id", "ip_version"],
|
||||
sort_dir=["asc", "desc"])
|
||||
|
||||
def test_list_address_scope_limit(self):
|
||||
@@ -130,9 +158,10 @@ class CLITestV20AddressScopeJSON(test_cli20.CLITestV20Base):
|
||||
resource = 'address_scope'
|
||||
cmd = address_scope.ShowAddressScope(
|
||||
test_cli20.MyApp(sys.stdout), None)
|
||||
args = ['--fields', 'id', '--fields', 'name', self.test_id]
|
||||
args = ['--fields', 'id', '--fields', 'name', self.test_id,
|
||||
'--fields', 'ip_version', '6']
|
||||
self._test_show_resource(resource, cmd, self.test_id, args,
|
||||
['id', 'name'])
|
||||
['id', 'name', 'ip_version'])
|
||||
|
||||
def test_delete_address_scope(self):
|
||||
# Delete address_scope: address_scope_id.
|
||||
|
||||
@@ -117,6 +117,21 @@ class CLITestV20FloatingIpsJSON(test_cli20.CLITestV20Base):
|
||||
self._test_create_resource(resource, cmd, name, myid, args,
|
||||
position_names, position_values)
|
||||
|
||||
def test_create_floatingip_with_dns_name_and_dns_domain(self):
|
||||
# Create floatingip: fip1 with dns name and dns domain.
|
||||
resource = 'floatingip'
|
||||
cmd = fip.CreateFloatingIP(test_cli20.MyApp(sys.stdout), None)
|
||||
name = 'fip1'
|
||||
myid = 'myid'
|
||||
dns_name_name = 'my-floatingip'
|
||||
dns_domain_name = 'my-domain.org.'
|
||||
args = [name, '--dns-name', dns_name_name, '--dns-domain',
|
||||
dns_domain_name]
|
||||
position_names = ['floating_network_id', 'dns_name', 'dns_domain']
|
||||
position_values = [name, dns_name_name, dns_domain_name]
|
||||
self._test_create_resource(resource, cmd, name, myid, args,
|
||||
position_names, position_values)
|
||||
|
||||
def test_list_floatingips(self):
|
||||
# list floatingips: -D.
|
||||
resources = 'floatingips'
|
||||
|
||||
@@ -163,6 +163,19 @@ class CLITestV20NetworkJSON(test_cli20.CLITestV20Base):
|
||||
self._test_create_resource(resource, cmd, name, myid, args,
|
||||
position_names, position_values)
|
||||
|
||||
def test_create_network_with_dns_domain(self):
|
||||
# Create net: --dns-domain my-domain.org.
|
||||
resource = 'network'
|
||||
cmd = network.CreateNetwork(test_cli20.MyApp(sys.stdout), None)
|
||||
name = 'myname'
|
||||
myid = 'myid'
|
||||
dns_domain_name = 'my-domain.org.'
|
||||
args = [name, '--dns-domain', dns_domain_name]
|
||||
position_names = ['name', 'dns_domain']
|
||||
position_values = [name, dns_domain_name]
|
||||
self._test_create_resource(resource, cmd, name, myid, args,
|
||||
position_names, position_values)
|
||||
|
||||
def test_list_nets_empty_with_column(self):
|
||||
resources = "networks"
|
||||
cmd = network.ListNetwork(test_cli20.MyApp(sys.stdout), None)
|
||||
@@ -196,13 +209,15 @@ class CLITestV20NetworkJSON(test_cli20.CLITestV20Base):
|
||||
|
||||
def _test_list_networks(self, cmd, detail=False, tags=(),
|
||||
fields_1=(), fields_2=(), page_size=None,
|
||||
sort_key=(), sort_dir=()):
|
||||
sort_key=(), sort_dir=(), base_args=None,
|
||||
query=''):
|
||||
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)
|
||||
sort_key=sort_key, sort_dir=sort_dir,
|
||||
base_args=base_args, query=query)
|
||||
|
||||
def test_list_nets_pagination(self):
|
||||
cmd = network.ListNetwork(test_cli20.MyApp(sys.stdout), None)
|
||||
@@ -288,7 +303,7 @@ class CLITestV20NetworkJSON(test_cli20.CLITestV20Base):
|
||||
args = []
|
||||
cmd_parser = cmd.get_parser('list_networks')
|
||||
parsed_args = cmd_parser.parse_args(args)
|
||||
result = cmd.get_data(parsed_args)
|
||||
result = cmd.take_action(parsed_args)
|
||||
self.mox.VerifyAll()
|
||||
self.mox.UnsetStubs()
|
||||
_result = [x for x in result[1]]
|
||||
@@ -526,6 +541,22 @@ class CLITestV20NetworkJSON(test_cli20.CLITestV20Base):
|
||||
['myid', '--no-qos-policy'],
|
||||
{'qos_policy_id': None, })
|
||||
|
||||
def test_update_network_with_dns_domain(self):
|
||||
# Update net: myid --dns-domain my-domain.org.
|
||||
resource = 'network'
|
||||
cmd = network.UpdateNetwork(test_cli20.MyApp(sys.stdout), None)
|
||||
self._test_update_resource(resource, cmd, 'myid',
|
||||
['myid', '--dns-domain', 'my-domain.org.'],
|
||||
{'dns_domain': 'my-domain.org.', })
|
||||
|
||||
def test_update_network_with_no_dns_domain(self):
|
||||
# Update net: myid --no-dns-domain
|
||||
resource = 'network'
|
||||
cmd = network.UpdateNetwork(test_cli20.MyApp(sys.stdout), None)
|
||||
self._test_update_resource(resource, cmd, 'myid',
|
||||
['myid', '--no-dns-domain'],
|
||||
{'dns_domain': "", })
|
||||
|
||||
def test_show_network(self):
|
||||
# Show net: --fields id --fields name myid.
|
||||
resource = 'network'
|
||||
@@ -611,3 +642,9 @@ class CLITestV20NetworkJSON(test_cli20.CLITestV20Base):
|
||||
'X-Auth-Token', test_cli20.TOKEN)).AndReturn(response)
|
||||
|
||||
self._test_extend_list(mox_calls)
|
||||
|
||||
def test_list_shared_networks(self):
|
||||
# list nets : --shared False
|
||||
cmd = network.ListNetwork(test_cli20.MyApp(sys.stdout), None)
|
||||
self._test_list_networks(cmd, base_args='--shared False'.split(),
|
||||
query='shared=False')
|
||||
|
||||
@@ -0,0 +1,54 @@
|
||||
# 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 neutronclient.neutron.v2_0 import network_ip_availability
|
||||
from neutronclient.tests.unit import test_cli20
|
||||
|
||||
|
||||
class CLITestV20NetworkIPAvailability(test_cli20.CLITestV20Base):
|
||||
|
||||
id_field = 'network_id'
|
||||
|
||||
def _test_list_network_ip_availability(self, args, query):
|
||||
resources = "network_ip_availabilities"
|
||||
cmd = network_ip_availability.ListIpAvailability(test_cli20.MyApp
|
||||
(sys.stdout), None)
|
||||
self._test_list_resources(resources, cmd,
|
||||
base_args=args,
|
||||
query=query)
|
||||
|
||||
def test_list_network_ip_availability(self):
|
||||
self._test_list_network_ip_availability(args=None,
|
||||
query='ip_version=4')
|
||||
|
||||
def test_list_network_ip_availability_ipv6(self):
|
||||
self._test_list_network_ip_availability(
|
||||
args=['--ip-version', '6'], query='ip_version=6')
|
||||
|
||||
def test_list_network_ip_availability_net_id_and_ipv4(self):
|
||||
self._test_list_network_ip_availability(
|
||||
args=['--ip-version', '4', '--network-id', 'myid'],
|
||||
query='ip_version=4&network_id=myid')
|
||||
|
||||
def test_list_network_ip_availability_net_name_and_tenant_id(self):
|
||||
self._test_list_network_ip_availability(
|
||||
args=['--network-name', 'foo', '--tenant-id', 'mytenant'],
|
||||
query='network_name=foo&tenant_id=mytenant&ip_version=4')
|
||||
|
||||
def test_show_network_ip_availability(self):
|
||||
resource = "network_ip_availability"
|
||||
cmd = network_ip_availability.ShowIpAvailability(
|
||||
test_cli20.MyApp(sys.stdout), None)
|
||||
self._test_show_resource(resource, cmd, self.test_id,
|
||||
args=[self.test_id])
|
||||
@@ -338,6 +338,20 @@ class CLITestV20PortJSON(test_cli20.CLITestV20Base):
|
||||
self._test_create_resource(resource, cmd, name, myid, args,
|
||||
position_names, position_values)
|
||||
|
||||
def test_create_port_with_dns_name(self):
|
||||
# Create port: --dns-name my-port.
|
||||
resource = 'port'
|
||||
cmd = port.CreatePort(test_cli20.MyApp(sys.stdout), None)
|
||||
name = 'myname'
|
||||
myid = 'myid'
|
||||
netid = 'netid'
|
||||
dns_name_name = 'my-port'
|
||||
args = [netid, '--dns-name', dns_name_name]
|
||||
position_names = ['network_id', 'dns_name']
|
||||
position_values = [netid, dns_name_name]
|
||||
self._test_create_resource(resource, cmd, name, myid, args,
|
||||
position_names, position_values)
|
||||
|
||||
def test_create_port_with_allowed_address_pair_ipaddr(self):
|
||||
# Create port:
|
||||
# --allowed-address-pair ip_address=addr0
|
||||
@@ -589,13 +603,14 @@ class CLITestV20PortJSON(test_cli20.CLITestV20Base):
|
||||
resource = 'port'
|
||||
cmd = port.UpdatePort(test_cli20.MyApp(sys.stdout), None)
|
||||
myid = 'myid'
|
||||
net_id = 'net_id'
|
||||
subnet_id = 'subnet_id'
|
||||
ip_addr = '123.123.123.123'
|
||||
args = [myid,
|
||||
'--fixed-ip', "network_id=%(net_id)s,ip_address=%(ip_addr)s" %
|
||||
{'net_id': net_id,
|
||||
'--fixed-ip',
|
||||
"subnet_id=%(subnet_id)s,ip_address=%(ip_addr)s" %
|
||||
{'subnet_id': subnet_id,
|
||||
'ip_addr': ip_addr}]
|
||||
updated_fields = {"fixed_ips": [{'network_id': net_id,
|
||||
updated_fields = {"fixed_ips": [{'subnet_id': subnet_id,
|
||||
'ip_address': ip_addr}]}
|
||||
self._test_update_resource(resource, cmd, myid, args, updated_fields)
|
||||
|
||||
@@ -648,6 +663,22 @@ class CLITestV20PortJSON(test_cli20.CLITestV20Base):
|
||||
['myid', '--no-qos-policy'],
|
||||
{'qos_policy_id': None, })
|
||||
|
||||
def test_update_port_with_dns_name(self):
|
||||
# Update port: myid --dns-name my-port.
|
||||
resource = 'port'
|
||||
cmd = port.UpdatePort(test_cli20.MyApp(sys.stdout), None)
|
||||
self._test_update_resource(resource, cmd, 'myid',
|
||||
['myid', '--dns-name', 'my-port'],
|
||||
{'dns_name': 'my-port', })
|
||||
|
||||
def test_update_port_with_no_dns_name(self):
|
||||
# Update port: myid --no-dns-name
|
||||
resource = 'port'
|
||||
cmd = port.UpdatePort(test_cli20.MyApp(sys.stdout), None)
|
||||
self._test_update_resource(resource, cmd, 'myid',
|
||||
['myid', '--no-dns-name'],
|
||||
{'dns_name': "", })
|
||||
|
||||
def test_delete_extra_dhcp_opts_from_port(self):
|
||||
resource = 'port'
|
||||
myid = 'myid'
|
||||
|
||||
100
neutronclient/tests/unit/test_cli20_purge.py
Normal file
100
neutronclient/tests/unit/test_cli20_purge.py
Normal file
@@ -0,0 +1,100 @@
|
||||
# Copyright 2016 Cisco Systems
|
||||
# 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 neutronclient.neutron.v2_0 import purge
|
||||
from neutronclient.tests.unit import test_cli20
|
||||
|
||||
|
||||
class CLITestV20Purge(test_cli20.CLITestV20Base):
|
||||
|
||||
def setUp(self):
|
||||
super(CLITestV20Purge, self).setUp()
|
||||
self.resource_types = ['floatingip', 'port', 'router',
|
||||
'network', 'security_group']
|
||||
|
||||
def _generate_resources_dict(self, value=0):
|
||||
resources_dict = {}
|
||||
resources_dict['true'] = value
|
||||
for resource_type in self.resource_types:
|
||||
resources_dict[resource_type] = value
|
||||
return resources_dict
|
||||
|
||||
def _verify_suffix(self, resources, message):
|
||||
for resource, value in resources.items():
|
||||
if value > 0:
|
||||
suffix = list('%(value)d %(resource)s' %
|
||||
{'value': value, 'resource': resource})
|
||||
if value != 1:
|
||||
suffix.append('s')
|
||||
suffix = ''.join(suffix)
|
||||
self.assertIn(suffix, message)
|
||||
else:
|
||||
self.assertNotIn(resource, message)
|
||||
|
||||
def _verify_message(self, message, deleted, failed):
|
||||
message = message.split('.')
|
||||
success_prefix = "Deleted "
|
||||
failure_prefix = "The following resources could not be deleted: "
|
||||
if not deleted['true']:
|
||||
for msg in message:
|
||||
self.assertNotIn(success_prefix, msg)
|
||||
message = message[0]
|
||||
if not failed['true']:
|
||||
expected = 'Tenant has no supported resources'
|
||||
self.assertEqual(expected, message)
|
||||
else:
|
||||
self.assertIn(failure_prefix, message)
|
||||
self._verify_suffix(failed, message)
|
||||
else:
|
||||
resources_deleted = message[0]
|
||||
self.assertIn(success_prefix, resources_deleted)
|
||||
self._verify_suffix(deleted, resources_deleted)
|
||||
if failed['true']:
|
||||
resources_failed = message[1]
|
||||
self.assertIn(failure_prefix, resources_failed)
|
||||
self._verify_suffix(failed, resources_failed)
|
||||
else:
|
||||
for msg in message:
|
||||
self.assertNotIn(failure_prefix, msg)
|
||||
|
||||
def _verify_result(self, my_purge, deleted, failed):
|
||||
message = my_purge._build_message(deleted, failed, failed['true'])
|
||||
self._verify_message(message, deleted, failed)
|
||||
|
||||
def test_build_message(self):
|
||||
my_purge = purge.Purge(test_cli20.MyApp(sys.stdout), None)
|
||||
|
||||
# Verify message when tenant has no supported resources
|
||||
deleted = self._generate_resources_dict()
|
||||
failed = self._generate_resources_dict()
|
||||
self._verify_result(my_purge, deleted, failed)
|
||||
|
||||
# Verify message when tenant has supported resources,
|
||||
# and they are all deleteable
|
||||
deleted = self._generate_resources_dict(1)
|
||||
self._verify_result(my_purge, deleted, failed)
|
||||
|
||||
# Verify message when tenant has supported resources,
|
||||
# and some are not deleteable
|
||||
failed = self._generate_resources_dict(1)
|
||||
self._verify_result(my_purge, deleted, failed)
|
||||
|
||||
# Verify message when tenant has supported resources,
|
||||
# and all are not deleteable
|
||||
deleted = self._generate_resources_dict()
|
||||
self._verify_result(my_purge, deleted, failed)
|
||||
@@ -15,41 +15,54 @@
|
||||
|
||||
import sys
|
||||
|
||||
import testscenarios
|
||||
|
||||
from neutronclient.neutron.v2_0 import rbac
|
||||
from neutronclient.tests.unit import test_cli20
|
||||
|
||||
load_tests = testscenarios.load_tests_apply_scenarios
|
||||
|
||||
class CLITestV20RBACJSON(test_cli20.CLITestV20Base):
|
||||
|
||||
class CLITestV20RBACBaseJSON(test_cli20.CLITestV20Base):
|
||||
non_admin_status_resources = ['rbac_policy']
|
||||
|
||||
scenarios = [
|
||||
('network rbac objects',
|
||||
{'object_type_name': 'network', 'object_type_val': 'network'}),
|
||||
('qos policy rbac objects',
|
||||
{'object_type_name': 'qos-policy', 'object_type_val': 'qos_policy'}),
|
||||
]
|
||||
|
||||
def test_create_rbac_policy_with_mandatory_params(self):
|
||||
# Create rbac: rbac_object --type network --action access_as_shared
|
||||
# Create rbac: rbac_object --type <object_type_name> --action
|
||||
# access_as_shared
|
||||
resource = 'rbac_policy'
|
||||
cmd = rbac.CreateRBACPolicy(test_cli20.MyApp(sys.stdout), None)
|
||||
name = 'rbac_object'
|
||||
myid = 'myid'
|
||||
args = [name, '--type', 'network',
|
||||
args = [name, '--type', self.object_type_name,
|
||||
'--action', 'access_as_shared']
|
||||
position_names = ['object_id', 'object_type',
|
||||
'target_tenant', 'action']
|
||||
position_values = [name, 'network', None, 'access_as_shared']
|
||||
position_values = [name, self.object_type_val, None,
|
||||
'access_as_shared']
|
||||
self._test_create_resource(resource, cmd, name, myid, args,
|
||||
position_names, position_values)
|
||||
|
||||
def test_create_rbac_policy_with_all_params(self):
|
||||
# Create rbac: rbac_object --type network --target-tenant tenant_id
|
||||
# --action access_as_external
|
||||
# Create rbac: rbac_object --type <object_type_name>
|
||||
# --target-tenant tenant_id --action access_as_external
|
||||
resource = 'rbac_policy'
|
||||
cmd = rbac.CreateRBACPolicy(test_cli20.MyApp(sys.stdout), None)
|
||||
name = 'rbac_object'
|
||||
myid = 'myid'
|
||||
args = [name, '--type', 'network',
|
||||
args = [name, '--type', self.object_type_name,
|
||||
'--target-tenant', 'tenant_id',
|
||||
'--action', 'access_as_external']
|
||||
position_names = ['object_id', 'object_type',
|
||||
'target_tenant', 'action']
|
||||
position_values = [name, 'network', 'tenant_id', 'access_as_external']
|
||||
position_values = [name, self.object_type_val, 'tenant_id',
|
||||
'access_as_external']
|
||||
self._test_create_resource(resource, cmd, name, myid, args,
|
||||
position_names, position_values)
|
||||
|
||||
@@ -59,12 +72,13 @@ class CLITestV20RBACJSON(test_cli20.CLITestV20Base):
|
||||
cmd = rbac.CreateRBACPolicy(test_cli20.MyApp(sys.stdout), None)
|
||||
name = u'\u7f51\u7edc'
|
||||
myid = 'myid'
|
||||
args = [name, '--type', 'network',
|
||||
args = [name, '--type', self.object_type_name,
|
||||
'--target-tenant', 'tenant_id',
|
||||
'--action', 'access_as_external']
|
||||
position_names = ['object_id', 'object_type',
|
||||
'target_tenant', 'action']
|
||||
position_values = [name, 'network', 'tenant_id', 'access_as_external']
|
||||
position_values = [name, self.object_type_val, 'tenant_id',
|
||||
'access_as_external']
|
||||
self._test_create_resource(resource, cmd, name, myid, args,
|
||||
position_names, position_values)
|
||||
|
||||
|
||||
@@ -383,6 +383,21 @@ class CLITestV20RouterJSON(test_cli20.CLITestV20Base):
|
||||
{"subnet_id": "mysubnet"}]}}
|
||||
)
|
||||
|
||||
def test_set_gateway_external_ip_and_subnet(self):
|
||||
# set external gateway for router: myid externalid --fixed-ip ...
|
||||
resource = 'router'
|
||||
cmd = router.SetGatewayRouter(test_cli20.MyApp(sys.stdout), None)
|
||||
args = ['myid', 'externalid', '--fixed-ip',
|
||||
'ip_address=10.0.0.2,subnet_id=mysubnet']
|
||||
self._test_update_resource(resource, cmd, 'myid',
|
||||
args,
|
||||
{"external_gateway_info":
|
||||
{"network_id": "externalid",
|
||||
"external_fixed_ips": [
|
||||
{"subnet_id": "mysubnet",
|
||||
"ip_address": "10.0.0.2"}]}}
|
||||
)
|
||||
|
||||
def test_remove_gateway(self):
|
||||
# Remove external gateway from router: externalid.
|
||||
resource = 'router'
|
||||
|
||||
@@ -407,7 +407,7 @@ class CLITestV20SecurityGroupsJSON(test_cli20.CLITestV20Base):
|
||||
|
||||
cmd_parser = cmd.get_parser('list_security_group_rules')
|
||||
parsed_args = cmd_parser.parse_args(args)
|
||||
result = cmd.get_data(parsed_args)
|
||||
result = cmd.take_action(parsed_args)
|
||||
self.mox.VerifyAll()
|
||||
self.mox.UnsetStubs()
|
||||
# Check columns
|
||||
|
||||
@@ -290,6 +290,25 @@ class CLITestV20SubnetJSON(test_cli20.CLITestV20Base):
|
||||
position_names, position_values,
|
||||
tenant_id='tenantid')
|
||||
|
||||
def test_create_subnet_with_use_default_subnetpool(self):
|
||||
# Create subnet: --tenant-id tenantid --use-default-subnetpool \
|
||||
# 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',
|
||||
'--use-default-subnetpool',
|
||||
netid, cidr]
|
||||
position_names = ['ip_version', 'use_default_subnetpool', 'network_id',
|
||||
'cidr']
|
||||
position_values = [4, True, 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'
|
||||
|
||||
@@ -63,6 +63,26 @@ class CLITestV20SubnetPoolJSON(test_cli20.CLITestV20Base):
|
||||
self._test_create_resource(resource, cmd, name, myid, args,
|
||||
position_names, position_values)
|
||||
|
||||
def test_create_subnetpool(self, default='false'):
|
||||
# Create subnetpool: myname.
|
||||
resource = 'subnetpool'
|
||||
cmd = subnetpool.CreateSubnetPool(test_cli20.MyApp(sys.stdout), None)
|
||||
name = 'myname'
|
||||
myid = 'myid'
|
||||
min_prefixlen = 30
|
||||
prefix1 = '10.11.12.0/24'
|
||||
prefix2 = '12.11.13.0/24'
|
||||
args = [name, '--min-prefixlen', str(min_prefixlen),
|
||||
'--pool-prefix', prefix1, '--pool-prefix', prefix2,
|
||||
'--is-default', default]
|
||||
position_names = ['name', 'min_prefixlen', 'prefixes', 'is_default']
|
||||
position_values = [name, min_prefixlen, [prefix1, prefix2], default]
|
||||
self._test_create_resource(resource, cmd, name, myid, args,
|
||||
position_names, position_values)
|
||||
|
||||
def test_create_subnetpool_default(self):
|
||||
self.test_create_subnetpool(default='true')
|
||||
|
||||
def test_create_subnetpool_with_unicode(self):
|
||||
# Create subnetpool: u'\u7f51\u7edc'.
|
||||
resource = 'subnetpool'
|
||||
|
||||
128
neutronclient/tests/unit/test_cli20_tag.py
Normal file
128
neutronclient/tests/unit/test_cli20_tag.py
Normal file
@@ -0,0 +1,128 @@
|
||||
# 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 mox3 import mox
|
||||
|
||||
from neutronclient.common import exceptions
|
||||
from neutronclient.neutron import v2_0 as neutronV2_0
|
||||
from neutronclient.neutron.v2_0 import network
|
||||
from neutronclient.neutron.v2_0 import tag
|
||||
from neutronclient import shell
|
||||
from neutronclient.tests.unit import test_cli20
|
||||
|
||||
|
||||
class CLITestV20Tag(test_cli20.CLITestV20Base):
|
||||
def _test_tag_operation(self, cmd, path, method, args, prog_name,
|
||||
body=None):
|
||||
self.mox.StubOutWithMock(cmd, "get_client")
|
||||
self.mox.StubOutWithMock(self.client.httpclient, "request")
|
||||
cmd.get_client().MultipleTimes().AndReturn(self.client)
|
||||
if body:
|
||||
body = test_cli20.MyComparator(body, self.client)
|
||||
self.client.httpclient.request(
|
||||
test_cli20.MyUrlComparator(
|
||||
test_cli20.end_url(path, format=self.format), self.client),
|
||||
method, body=body,
|
||||
headers=mox.ContainsKeyValue(
|
||||
'X-Auth-Token', test_cli20.TOKEN)).AndReturn(
|
||||
(test_cli20.MyResp(204), None))
|
||||
self.mox.ReplayAll()
|
||||
cmd_parser = cmd.get_parser(prog_name)
|
||||
shell.run_command(cmd, cmd_parser, args)
|
||||
self.mox.VerifyAll()
|
||||
self.mox.UnsetStubs()
|
||||
|
||||
def _test_tags_query(self, cmd, resources, args, query):
|
||||
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")
|
||||
res = {resources: [{'id': 'myid'}]}
|
||||
resstr = self.client.serialize(res)
|
||||
self.client.httpclient.request(
|
||||
test_cli20.MyUrlComparator(
|
||||
test_cli20.end_url(path, query, format=self.format),
|
||||
self.client),
|
||||
'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_networks")
|
||||
shell.run_command(cmd, cmd_parser, args)
|
||||
self.mox.VerifyAll()
|
||||
self.mox.UnsetStubs()
|
||||
_str = self.fake_stdout.make_string()
|
||||
self.assertIn('myid', _str)
|
||||
|
||||
def _make_tag_path(self, resource, resource_id, tag):
|
||||
path = getattr(self.client, "tag_path")
|
||||
resource_plural = neutronV2_0._get_resource_plural(resource,
|
||||
self.client)
|
||||
return path % (resource_plural, resource_id, tag)
|
||||
|
||||
def _make_tags_path(self, resource, resource_id):
|
||||
path = getattr(self.client, "tags_path")
|
||||
resource_plural = neutronV2_0._get_resource_plural(resource,
|
||||
self.client)
|
||||
return path % (resource_plural, resource_id)
|
||||
|
||||
def test_add_tag(self):
|
||||
cmd = tag.AddTag(test_cli20.MyApp(sys.stdout), None)
|
||||
path = self._make_tag_path('network', 'myid', 'red')
|
||||
args = ['--resource-type', 'network', '--resource', 'myid',
|
||||
'--tag', 'red']
|
||||
self._test_tag_operation(cmd, path, 'PUT', args, "tag-add")
|
||||
|
||||
def test_replace_tag(self):
|
||||
cmd = tag.ReplaceTag(test_cli20.MyApp(sys.stdout), None)
|
||||
path = self._make_tags_path('network', 'myid')
|
||||
args = ['--resource-type', 'network', '--resource', 'myid',
|
||||
'--tag', 'red', '--tag', 'blue']
|
||||
body = {'tags': ['red', 'blue']}
|
||||
self._test_tag_operation(cmd, path, 'PUT', args, "tag-replace",
|
||||
body=body)
|
||||
|
||||
def test_remove_tag(self):
|
||||
cmd = tag.RemoveTag(test_cli20.MyApp(sys.stdout), None)
|
||||
path = self._make_tag_path('network', 'myid', 'red')
|
||||
args = ['--resource-type', 'network', '--resource', 'myid',
|
||||
'--tag', 'red']
|
||||
self._test_tag_operation(cmd, path, 'DELETE', args, "tag-remove")
|
||||
|
||||
def test_remove_tag_all(self):
|
||||
cmd = tag.RemoveTag(test_cli20.MyApp(sys.stdout), None)
|
||||
path = self._make_tags_path('network', 'myid')
|
||||
args = ['--resource-type', 'network', '--resource', 'myid',
|
||||
'--all']
|
||||
self._test_tag_operation(cmd, path, 'DELETE', args, "tag-remove")
|
||||
|
||||
def test_no_tag_nor_all(self):
|
||||
cmd = tag.RemoveTag(test_cli20.MyApp(sys.stdout), None)
|
||||
path = self._make_tags_path('network', 'myid')
|
||||
args = ['--resource-type', 'network', '--resource', 'myid']
|
||||
self.assertRaises(exceptions.CommandError, self._test_tag_operation,
|
||||
cmd, path, 'DELETE', args, "tag-remove")
|
||||
|
||||
def test_tags_query(self):
|
||||
# This test examines that '-' in the tag related filters
|
||||
# is not converted to '_'.
|
||||
resources = 'networks'
|
||||
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())
|
||||
args = ['--not-tags', 'red,blue', '--tags-any', 'green',
|
||||
'--not-tags-any', 'black']
|
||||
query = "not-tags=red,blue&tags-any=green¬-tags-any=black"
|
||||
self._test_tags_query(cmd, resources, args, query)
|
||||
@@ -104,6 +104,11 @@ class CLITestV20ExtensionJSON(test_cli20.CLITestV20Base):
|
||||
cmd = fox_sockets.FoxInSocketsList(test_cli20.MyApp(sys.stdout), None)
|
||||
self._test_list_resources(resources, cmd, True)
|
||||
|
||||
def test_list_fox_pagination(self):
|
||||
resources = 'fox_sockets'
|
||||
cmd = fox_sockets.FoxInSocketsList(test_cli20.MyApp(sys.stdout), None)
|
||||
self._test_list_resources_with_pagination(resources, cmd)
|
||||
|
||||
def test_show_fox_socket(self):
|
||||
# Show fox_socket: --fields id --fields name myid.
|
||||
resource = 'fox_socket'
|
||||
|
||||
@@ -144,7 +144,7 @@ class CLITestNameorID(testtools.TestCase):
|
||||
try:
|
||||
neutronV20.find_resourceid_by_name_or_id(
|
||||
self.client, 'network', name)
|
||||
except exceptions.NeutronClientException as ex:
|
||||
except exceptions.NotFound as ex:
|
||||
self.assertIn('Unable to find', ex.message)
|
||||
self.assertEqual(404, ex.status_code)
|
||||
|
||||
@@ -193,6 +193,6 @@ class CLITestNameorID(testtools.TestCase):
|
||||
try:
|
||||
neutronV20.find_resourceid_by_name_or_id(
|
||||
self.client, 'security_group', name, project)
|
||||
except exceptions.NeutronClientException as ex:
|
||||
except exceptions.NotFound as ex:
|
||||
self.assertIn('Unable to find', ex.message)
|
||||
self.assertEqual(404, ex.status_code)
|
||||
|
||||
@@ -49,6 +49,36 @@ class TestUtils(testtools.TestCase):
|
||||
self.assertRaises(argparse.ArgumentTypeError,
|
||||
utils.str2dict, input_str)
|
||||
|
||||
def test_str2dict_optional_keys(self):
|
||||
self.assertDictEqual({'key1': 'value1'},
|
||||
utils.str2dict('key1=value1',
|
||||
optional_keys=['key1', 'key2']))
|
||||
self.assertDictEqual({'key1': 'value1', 'key2': 'value2'},
|
||||
utils.str2dict('key1=value1,key2=value2',
|
||||
optional_keys=['key1', 'key2']))
|
||||
e = self.assertRaises(argparse.ArgumentTypeError,
|
||||
utils.str2dict,
|
||||
'key1=value1,key2=value2,key3=value3',
|
||||
optional_keys=['key1', 'key2'])
|
||||
self.assertEqual("Invalid key(s) 'key3' specified. "
|
||||
"Valid key(s): 'key1, key2'.",
|
||||
str(e))
|
||||
|
||||
def test_str2dict_required_keys(self):
|
||||
self.assertDictEqual(
|
||||
{'key1': 'value1', 'key2': 'value2', 'key3': 'value3'},
|
||||
utils.str2dict('key1=value1,key2=value2,key3=value3',
|
||||
required_keys=['key1', 'key2'],
|
||||
optional_keys=['key3']))
|
||||
self.assertDictEqual(
|
||||
{'key1': 'value1', 'key2': 'value2'},
|
||||
utils.str2dict('key1=value1,key2=value2',
|
||||
required_keys=['key1', 'key2']))
|
||||
e = self.assertRaises(argparse.ArgumentTypeError,
|
||||
utils.str2dict, 'key1=value1',
|
||||
required_keys=['key1', 'key2'])
|
||||
self.assertEqual("Required key(s) 'key2' not specified.", str(e))
|
||||
|
||||
def test_get_dict_item_properties(self):
|
||||
item = {'name': 'test_name', 'id': 'test_id'}
|
||||
fields = ('name', 'id')
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
|
||||
import sys
|
||||
|
||||
from neutronclient.common import exceptions
|
||||
from neutronclient.neutron.v2_0.vpn import ikepolicy
|
||||
from neutronclient.tests.unit import test_cli20
|
||||
|
||||
@@ -101,7 +102,7 @@ class CLITestV20VpnIkePolicyJSON(test_cli20.CLITestV20Base):
|
||||
self._test_create_resource(resource, cmd, name, my_id, args,
|
||||
position_names, position_values)
|
||||
|
||||
def _test_lifetime_values(self, lifetime):
|
||||
def _test_lifetime_values(self, lifetime, expected_exc=None):
|
||||
resource = 'ikepolicy'
|
||||
cmd = ikepolicy.CreateIKEPolicy(test_cli20.MyApp(sys.stdout), None)
|
||||
name = 'ikepolicy1'
|
||||
@@ -134,16 +135,17 @@ class CLITestV20VpnIkePolicyJSON(test_cli20.CLITestV20Base):
|
||||
auth_algorithm, encryption_algorithm,
|
||||
phase1_negotiation_mode, ike_version, pfs,
|
||||
tenant_id]
|
||||
try:
|
||||
self._test_create_resource(resource, cmd, name, my_id, args,
|
||||
position_names, position_values)
|
||||
except Exception:
|
||||
return
|
||||
self.fail("IKEPolicy Lifetime Error")
|
||||
if not expected_exc:
|
||||
expected_exc = exceptions.CommandError
|
||||
self.assertRaises(
|
||||
expected_exc,
|
||||
self._test_create_resource,
|
||||
resource, cmd, name, my_id, args,
|
||||
position_names, position_values)
|
||||
|
||||
def test_create_ikepolicy_with_invalid_lifetime_keys(self):
|
||||
lifetime = 'uts=seconds,val=20000'
|
||||
self._test_lifetime_values(lifetime)
|
||||
self._test_lifetime_values(lifetime, expected_exc=SystemExit)
|
||||
|
||||
def test_create_ikepolicy_with_invalid_lifetime_value(self):
|
||||
lifetime = 'units=seconds,value=-1'
|
||||
|
||||
@@ -180,7 +180,7 @@ class CLITestV20IPsecSiteConnectionJSON(test_cli20.CLITestV20Base):
|
||||
self._test_create_resource(resource, cmd, None, my_id, args,
|
||||
position_names, position_values)
|
||||
|
||||
def _test_create_failure(self, additional_args=None):
|
||||
def _test_create_failure(self, additional_args=None, expected_exc=None):
|
||||
# Helper to test failure of IPSec site-to-site creation failure.
|
||||
resource = 'ipsec_site_connection'
|
||||
cmd = ipsec_site_connection.CreateIPsecSiteConnection(
|
||||
@@ -215,7 +215,9 @@ class CLITestV20IPsecSiteConnectionJSON(test_cli20.CLITestV20Base):
|
||||
position_values = [tenant_id, admin_state, peer_address, peer_id, psk,
|
||||
mtu, initiator, None, None, vpnservice_id,
|
||||
ikepolicy_id, ipsecpolicy_id]
|
||||
self.assertRaises(exceptions.CommandError,
|
||||
if not expected_exc:
|
||||
expected_exc = exceptions.CommandError
|
||||
self.assertRaises(expected_exc,
|
||||
self._test_create_resource,
|
||||
resource, cmd, None, my_id, args,
|
||||
position_names, position_values)
|
||||
@@ -227,7 +229,7 @@ class CLITestV20IPsecSiteConnectionJSON(test_cli20.CLITestV20Base):
|
||||
|
||||
def test_fail_create_with_invalid_dpd_keys(self):
|
||||
bad_dpd_key = ['--dpd', 'act=restart,interval=30,time=120']
|
||||
self._test_create_failure(bad_dpd_key)
|
||||
self._test_create_failure(bad_dpd_key, SystemExit)
|
||||
|
||||
def test_fail_create_with_invalid_dpd_values(self):
|
||||
bad_dpd_values = ['--dpd', 'action=hold,interval=30,timeout=-1']
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
|
||||
import sys
|
||||
|
||||
from neutronclient.common import exceptions
|
||||
from neutronclient.neutron.v2_0.vpn import ipsecpolicy
|
||||
from neutronclient.tests.unit import test_cli20
|
||||
|
||||
@@ -98,7 +99,7 @@ class CLITestV20VpnIpsecPolicyJSON(test_cli20.CLITestV20Base):
|
||||
self._test_create_resource(resource, cmd, name, my_id, args,
|
||||
position_names, position_values)
|
||||
|
||||
def _test_lifetime_values(self, lifetime):
|
||||
def _test_lifetime_values(self, lifetime, expected_exc=None):
|
||||
resource = 'ipsecpolicy'
|
||||
cmd = ipsecpolicy.CreateIPsecPolicy(test_cli20.MyApp(sys.stdout), None)
|
||||
name = 'ipsecpolicy1'
|
||||
@@ -131,19 +132,24 @@ class CLITestV20VpnIpsecPolicyJSON(test_cli20.CLITestV20Base):
|
||||
auth_algorithm, encryption_algorithm,
|
||||
phase1_negotiation_mode, ike_version, pfs,
|
||||
tenant_id]
|
||||
try:
|
||||
self._test_create_resource(resource, cmd, name, my_id, args,
|
||||
position_names, position_values)
|
||||
except Exception:
|
||||
return
|
||||
self.fail("IPsecPolicy Lifetime Error")
|
||||
if not expected_exc:
|
||||
expected_exc = exceptions.CommandError
|
||||
self.assertRaises(
|
||||
expected_exc,
|
||||
self._test_create_resource,
|
||||
resource, cmd, name, my_id, args,
|
||||
position_names, position_values)
|
||||
|
||||
def test_create_ipsecpolicy_with_invalid_lifetime_keys(self):
|
||||
lifetime = 'uts=seconds,val=20000'
|
||||
self._test_lifetime_values(lifetime, SystemExit)
|
||||
|
||||
def test_create_ipsecpolicy_with_invalid_lifetime_units(self):
|
||||
lifetime = 'units=minutes,value=600'
|
||||
self._test_lifetime_values(lifetime)
|
||||
|
||||
def test_create_ipsecpolicy_with_invalide_lifetime_values(self):
|
||||
lifetime = 'units=minutes,value=0'
|
||||
def test_create_ipsecpolicy_with_invalid_lifetime_value(self):
|
||||
lifetime = 'units=seconds,value=0'
|
||||
self._test_lifetime_values(lifetime)
|
||||
|
||||
def test_list_ipsecpolicy(self):
|
||||
|
||||
@@ -22,6 +22,7 @@ import time
|
||||
|
||||
import requests
|
||||
import six.moves.urllib.parse as urlparse
|
||||
from six import string_types
|
||||
|
||||
from neutronclient._i18n import _
|
||||
from neutronclient import client
|
||||
@@ -44,10 +45,11 @@ def exception_handler_v20(status_code, error_content):
|
||||
:param error_content: deserialized body of error response
|
||||
"""
|
||||
error_dict = None
|
||||
request_ids = error_content.request_ids
|
||||
if isinstance(error_content, dict):
|
||||
error_dict = error_content.get('NeutronError')
|
||||
# Find real error type
|
||||
bad_neutron_error_flag = False
|
||||
client_exc = None
|
||||
if error_dict:
|
||||
# If Neutron key is found, it will definitely contain
|
||||
# a 'message' and 'type' keys?
|
||||
@@ -56,39 +58,34 @@ def exception_handler_v20(status_code, error_content):
|
||||
error_message = error_dict['message']
|
||||
if error_dict['detail']:
|
||||
error_message += "\n" + error_dict['detail']
|
||||
except Exception:
|
||||
bad_neutron_error_flag = True
|
||||
if not bad_neutron_error_flag:
|
||||
# If corresponding exception is defined, use it.
|
||||
client_exc = getattr(exceptions, '%sClient' % error_type, None)
|
||||
# Otherwise look up per status-code client exception
|
||||
if not client_exc:
|
||||
client_exc = exceptions.HTTP_EXCEPTION_MAP.get(status_code)
|
||||
if client_exc:
|
||||
raise client_exc(message=error_message,
|
||||
status_code=status_code)
|
||||
else:
|
||||
raise exceptions.NeutronClientException(
|
||||
status_code=status_code, message=error_message)
|
||||
else:
|
||||
raise exceptions.NeutronClientException(status_code=status_code,
|
||||
message=error_dict)
|
||||
except Exception:
|
||||
error_message = "%s" % error_dict
|
||||
else:
|
||||
message = None
|
||||
error_message = None
|
||||
if isinstance(error_content, dict):
|
||||
message = error_content.get('message')
|
||||
if message:
|
||||
raise exceptions.NeutronClientException(status_code=status_code,
|
||||
message=message)
|
||||
error_message = error_content.get('message')
|
||||
if not error_message:
|
||||
# If we end up here the exception was not a neutron error
|
||||
error_message = "%s-%s" % (status_code, error_content)
|
||||
|
||||
# If we end up here the exception was not a neutron error
|
||||
msg = "%s-%s" % (status_code, error_content)
|
||||
raise exceptions.NeutronClientException(status_code=status_code,
|
||||
message=msg)
|
||||
# If an exception corresponding to the error type is not found,
|
||||
# look up per status-code client exception.
|
||||
if not client_exc:
|
||||
client_exc = exceptions.HTTP_EXCEPTION_MAP.get(status_code)
|
||||
# If there is no exception per status-code,
|
||||
# Use NeutronClientException as fallback.
|
||||
if not client_exc:
|
||||
client_exc = exceptions.NeutronClientException
|
||||
|
||||
raise client_exc(message=error_message,
|
||||
status_code=status_code,
|
||||
request_ids=request_ids)
|
||||
|
||||
|
||||
class APIParamsCall(object):
|
||||
"""A Decorator to support formating and tenant overriding and filters."""
|
||||
"""A Decorator to support formatting and tenant overriding and filters."""
|
||||
def __init__(self, function):
|
||||
self.function = function
|
||||
|
||||
@@ -103,6 +100,99 @@ class APIParamsCall(object):
|
||||
return with_params
|
||||
|
||||
|
||||
class _RequestIdMixin(object):
|
||||
"""Wrapper class to expose x-openstack-request-id to the caller."""
|
||||
def _request_ids_setup(self):
|
||||
self._request_ids = []
|
||||
|
||||
@property
|
||||
def request_ids(self):
|
||||
return self._request_ids
|
||||
|
||||
def _append_request_ids(self, resp):
|
||||
"""Add request_ids as an attribute to the object
|
||||
|
||||
:param resp: Response object or list of Response objects
|
||||
"""
|
||||
if isinstance(resp, list):
|
||||
# Add list of request_ids if response is of type list.
|
||||
for resp_obj in resp:
|
||||
self._append_request_id(resp_obj)
|
||||
elif resp is not None:
|
||||
# Add request_ids if response contains single object.
|
||||
self._append_request_id(resp)
|
||||
|
||||
def _append_request_id(self, resp):
|
||||
if isinstance(resp, requests.Response):
|
||||
# Extract 'x-openstack-request-id' from headers if
|
||||
# response is a Response object.
|
||||
request_id = resp.headers.get('x-openstack-request-id')
|
||||
else:
|
||||
# If resp is of type string.
|
||||
request_id = resp
|
||||
if request_id:
|
||||
self._request_ids.append(request_id)
|
||||
|
||||
|
||||
class _DictWithMeta(dict, _RequestIdMixin):
|
||||
def __init__(self, values, resp):
|
||||
super(_DictWithMeta, self).__init__(values)
|
||||
self._request_ids_setup()
|
||||
self._append_request_ids(resp)
|
||||
|
||||
|
||||
class _TupleWithMeta(tuple, _RequestIdMixin):
|
||||
def __new__(cls, values, resp):
|
||||
return super(_TupleWithMeta, cls).__new__(cls, values)
|
||||
|
||||
def __init__(self, values, resp):
|
||||
self._request_ids_setup()
|
||||
self._append_request_ids(resp)
|
||||
|
||||
|
||||
class _StrWithMeta(str, _RequestIdMixin):
|
||||
def __new__(cls, value, resp):
|
||||
return super(_StrWithMeta, cls).__new__(cls, value)
|
||||
|
||||
def __init__(self, values, resp):
|
||||
self._request_ids_setup()
|
||||
self._append_request_ids(resp)
|
||||
|
||||
|
||||
class _GeneratorWithMeta(_RequestIdMixin):
|
||||
def __init__(self, paginate_func, collection, path, **params):
|
||||
self.paginate_func = paginate_func
|
||||
self.collection = collection
|
||||
self.path = path
|
||||
self.params = params
|
||||
self.generator = None
|
||||
self._request_ids_setup()
|
||||
|
||||
def _paginate(self):
|
||||
for r in self.paginate_func(
|
||||
self.collection, self.path, **self.params):
|
||||
yield r, r.request_ids
|
||||
|
||||
def __iter__(self):
|
||||
return self
|
||||
|
||||
# Python 3 compatibility
|
||||
def __next__(self):
|
||||
return self.next()
|
||||
|
||||
def next(self):
|
||||
if not self.generator:
|
||||
self.generator = self._paginate()
|
||||
|
||||
try:
|
||||
obj, req_id = next(self.generator)
|
||||
self._append_request_ids(req_id)
|
||||
except StopIteration:
|
||||
raise StopIteration()
|
||||
|
||||
return obj
|
||||
|
||||
|
||||
class ClientBase(object):
|
||||
"""Client for the OpenStack Neutron v2.0 API.
|
||||
|
||||
@@ -168,7 +258,7 @@ class ClientBase(object):
|
||||
self.action_prefix = "/v%s" % (self.version)
|
||||
self.retry_interval = 1
|
||||
|
||||
def _handle_fault_response(self, status_code, response_body):
|
||||
def _handle_fault_response(self, status_code, response_body, resp):
|
||||
# Create exception with HTTP status code and message
|
||||
_logger.debug("Error message: %s", response_body)
|
||||
# Add deserialized error message to exception arguments
|
||||
@@ -178,14 +268,15 @@ class ClientBase(object):
|
||||
# If unable to deserialized body it is probably not a
|
||||
# Neutron error
|
||||
des_error_body = {'message': response_body}
|
||||
error_body = self._convert_into_with_meta(des_error_body, resp)
|
||||
# Raise the appropriate exception
|
||||
exception_handler_v20(status_code, des_error_body)
|
||||
exception_handler_v20(status_code, error_body)
|
||||
|
||||
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:
|
||||
if isinstance(params, dict) and params:
|
||||
params = utils.safe_encode_dict(params)
|
||||
action += '?' + urlparse.urlencode(params, doseq=1)
|
||||
|
||||
@@ -199,11 +290,12 @@ class ClientBase(object):
|
||||
requests.codes.created,
|
||||
requests.codes.accepted,
|
||||
requests.codes.no_content):
|
||||
return self.deserialize(replybody, status_code)
|
||||
data = self.deserialize(replybody, status_code)
|
||||
return self._convert_into_with_meta(data, resp)
|
||||
else:
|
||||
if not replybody:
|
||||
replybody = resp.reason
|
||||
self._handle_fault_response(status_code, replybody)
|
||||
self._handle_fault_response(status_code, replybody, resp)
|
||||
|
||||
def get_auth_info(self):
|
||||
return self.httpclient.get_auth_info()
|
||||
@@ -216,7 +308,7 @@ class ClientBase(object):
|
||||
"""
|
||||
if data is None:
|
||||
return None
|
||||
elif type(data) is dict:
|
||||
elif isinstance(data, dict):
|
||||
return serializer.Serializer().serialize(data)
|
||||
else:
|
||||
raise Exception(_("Unable to serialize object of type = '%s'") %
|
||||
@@ -277,11 +369,14 @@ class ClientBase(object):
|
||||
def list(self, collection, path, retrieve_all=True, **params):
|
||||
if retrieve_all:
|
||||
res = []
|
||||
request_ids = []
|
||||
for r in self._pagination(collection, path, **params):
|
||||
res.extend(r[collection])
|
||||
return {collection: res}
|
||||
request_ids.extend(r.request_ids)
|
||||
return _DictWithMeta({collection: res}, request_ids)
|
||||
else:
|
||||
return self._pagination(collection, path, **params)
|
||||
return _GeneratorWithMeta(self._pagination, collection,
|
||||
path, **params)
|
||||
|
||||
def _pagination(self, collection, path, **params):
|
||||
if params.get('page_reverse', False):
|
||||
@@ -303,6 +398,15 @@ class ClientBase(object):
|
||||
except KeyError:
|
||||
break
|
||||
|
||||
def _convert_into_with_meta(self, item, resp):
|
||||
if item:
|
||||
if isinstance(item, dict):
|
||||
return _DictWithMeta(item, resp)
|
||||
elif isinstance(item, string_types):
|
||||
return _StrWithMeta(item, resp)
|
||||
else:
|
||||
return _TupleWithMeta((), resp)
|
||||
|
||||
|
||||
class Client(ClientBase):
|
||||
|
||||
@@ -342,8 +446,13 @@ class Client(ClientBase):
|
||||
lbaas_loadbalancers_path = "/lbaas/loadbalancers"
|
||||
lbaas_loadbalancer_path = "/lbaas/loadbalancers/%s"
|
||||
lbaas_loadbalancer_path_stats = "/lbaas/loadbalancers/%s/stats"
|
||||
lbaas_loadbalancer_path_status = "/lbaas/loadbalancers/%s/statuses"
|
||||
lbaas_listeners_path = "/lbaas/listeners"
|
||||
lbaas_listener_path = "/lbaas/listeners/%s"
|
||||
lbaas_l7policies_path = "/lbaas/l7policies"
|
||||
lbaas_l7policy_path = lbaas_l7policies_path + "/%s"
|
||||
lbaas_l7rules_path = lbaas_l7policy_path + "/rules"
|
||||
lbaas_l7rule_path = lbaas_l7rules_path + "/%s"
|
||||
lbaas_pools_path = "/lbaas/pools"
|
||||
lbaas_pool_path = "/lbaas/pools/%s"
|
||||
lbaas_healthmonitors_path = "/lbaas/healthmonitors"
|
||||
@@ -408,6 +517,19 @@ class Client(ClientBase):
|
||||
flavor_profile_bindings_path = flavor_path + service_profiles_path
|
||||
flavor_profile_binding_path = flavor_path + service_profile_path
|
||||
availability_zones_path = "/availability_zones"
|
||||
auto_allocated_topology_path = "/auto-allocated-topology/%s"
|
||||
BGP_DRINSTANCES = "/bgp-drinstances"
|
||||
BGP_DRINSTANCE = "/bgp-drinstance/%s"
|
||||
BGP_DRAGENTS = "/bgp-dragents"
|
||||
BGP_DRAGENT = "/bgp-dragents/%s"
|
||||
bgp_speakers_path = "/bgp-speakers"
|
||||
bgp_speaker_path = "/bgp-speakers/%s"
|
||||
bgp_peers_path = "/bgp-peers"
|
||||
bgp_peer_path = "/bgp-peers/%s"
|
||||
network_ip_availabilities_path = '/network-ip-availabilities'
|
||||
network_ip_availability_path = '/network-ip-availabilities/%s'
|
||||
tags_path = "/%s/%s/tags"
|
||||
tag_path = "/%s/%s/tags/%s"
|
||||
|
||||
# API has no way to report plurals, so we have to hard code them
|
||||
EXTED_PLURALS = {'routers': 'router',
|
||||
@@ -434,6 +556,9 @@ class Client(ClientBase):
|
||||
'metering_label_rules': 'metering_label_rule',
|
||||
'loadbalancers': 'loadbalancer',
|
||||
'listeners': 'listener',
|
||||
'l7rules': 'l7rule',
|
||||
'l7policies': 'l7policy',
|
||||
'lbaas_l7policies': 'lbaas_l7policy',
|
||||
'lbaas_pools': 'lbaas_pool',
|
||||
'lbaas_healthmonitors': 'lbaas_healthmonitor',
|
||||
'lbaas_members': 'lbaas_member',
|
||||
@@ -443,33 +568,37 @@ class Client(ClientBase):
|
||||
'qos_policies': 'qos_policy',
|
||||
'policies': 'policy',
|
||||
'bandwidth_limit_rules': 'bandwidth_limit_rule',
|
||||
'rules': 'rule',
|
||||
'rule_types': 'rule_type',
|
||||
'flavors': 'flavor',
|
||||
'bgp_speakers': 'bgp_speaker',
|
||||
'bgp_peers': 'bgp_peer',
|
||||
'network_ip_availabilities': 'network_ip_availability',
|
||||
}
|
||||
|
||||
@APIParamsCall
|
||||
def list_ext(self, path, **_params):
|
||||
"""Client extension hook for lists."""
|
||||
return self.get(path, params=_params)
|
||||
def list_ext(self, collection, path, retrieve_all, **_params):
|
||||
"""Client extension hook for list."""
|
||||
return self.list(collection, path, retrieve_all, **_params)
|
||||
|
||||
@APIParamsCall
|
||||
def show_ext(self, path, id, **_params):
|
||||
"""Client extension hook for shows."""
|
||||
"""Client extension hook for show."""
|
||||
return self.get(path % id, params=_params)
|
||||
|
||||
@APIParamsCall
|
||||
def create_ext(self, path, body=None):
|
||||
"""Client extension hook for creates."""
|
||||
"""Client extension hook for create."""
|
||||
return self.post(path, body=body)
|
||||
|
||||
@APIParamsCall
|
||||
def update_ext(self, path, id, body=None):
|
||||
"""Client extension hook for updates."""
|
||||
"""Client extension hook for update."""
|
||||
return self.put(path % id, body=body)
|
||||
|
||||
@APIParamsCall
|
||||
def delete_ext(self, path, id):
|
||||
"""Client extension hook for deletes."""
|
||||
"""Client extension hook for delete."""
|
||||
return self.delete(path % id)
|
||||
|
||||
@APIParamsCall
|
||||
@@ -499,24 +628,24 @@ class Client(ClientBase):
|
||||
|
||||
@APIParamsCall
|
||||
def list_extensions(self, **_params):
|
||||
"""Fetch a list of all exts on server side."""
|
||||
"""Fetch a list of all extensions 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."""
|
||||
"""Fetches information of a certain extension."""
|
||||
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."""
|
||||
"""Fetches a list of all ports 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."""
|
||||
"""Fetches information of a certain port."""
|
||||
return self.get(self.port_path % (port), params=_params)
|
||||
|
||||
@APIParamsCall
|
||||
@@ -563,7 +692,7 @@ class Client(ClientBase):
|
||||
|
||||
@APIParamsCall
|
||||
def list_subnets(self, retrieve_all=True, **_params):
|
||||
"""Fetches a list of all networks for a tenant."""
|
||||
"""Fetches a list of all subnets for a tenant."""
|
||||
return self.list('subnets', self.subnets_path, retrieve_all,
|
||||
**_params)
|
||||
|
||||
@@ -944,6 +1073,12 @@ class Client(ClientBase):
|
||||
return self.get(self.lbaas_loadbalancer_path_stats % (loadbalancer),
|
||||
params=_params)
|
||||
|
||||
@APIParamsCall
|
||||
def retrieve_loadbalancer_status(self, loadbalancer, **_params):
|
||||
"""Retrieves status for a certain load balancer."""
|
||||
return self.get(self.lbaas_loadbalancer_path_status % (loadbalancer),
|
||||
params=_params)
|
||||
|
||||
@APIParamsCall
|
||||
def list_listeners(self, retrieve_all=True, **_params):
|
||||
"""Fetches a list of all lbaas_listeners for a tenant."""
|
||||
@@ -972,6 +1107,62 @@ class Client(ClientBase):
|
||||
"""Deletes the specified lbaas_listener."""
|
||||
return self.delete(self.lbaas_listener_path % (lbaas_listener))
|
||||
|
||||
@APIParamsCall
|
||||
def list_lbaas_l7policies(self, retrieve_all=True, **_params):
|
||||
"""Fetches a list of all L7 policies for a listener."""
|
||||
return self.list('l7policies', self.lbaas_l7policies_path,
|
||||
retrieve_all, **_params)
|
||||
|
||||
@APIParamsCall
|
||||
def show_lbaas_l7policy(self, l7policy, **_params):
|
||||
"""Fetches information of a certain listener's L7 policy."""
|
||||
return self.get(self.lbaas_l7policy_path % l7policy,
|
||||
params=_params)
|
||||
|
||||
@APIParamsCall
|
||||
def create_lbaas_l7policy(self, body=None):
|
||||
"""Creates L7 policy for a certain listener."""
|
||||
return self.post(self.lbaas_l7policies_path, body=body)
|
||||
|
||||
@APIParamsCall
|
||||
def update_lbaas_l7policy(self, l7policy, body=None):
|
||||
"""Updates L7 policy."""
|
||||
return self.put(self.lbaas_l7policy_path % l7policy,
|
||||
body=body)
|
||||
|
||||
@APIParamsCall
|
||||
def delete_lbaas_l7policy(self, l7policy):
|
||||
"""Deletes the specified L7 policy."""
|
||||
return self.delete(self.lbaas_l7policy_path % l7policy)
|
||||
|
||||
@APIParamsCall
|
||||
def list_lbaas_l7rules(self, l7policy, retrieve_all=True, **_params):
|
||||
"""Fetches a list of all rules for L7 policy."""
|
||||
return self.list('rules', self.lbaas_l7rules_path % l7policy,
|
||||
retrieve_all, **_params)
|
||||
|
||||
@APIParamsCall
|
||||
def show_lbaas_l7rule(self, l7rule, l7policy, **_params):
|
||||
"""Fetches information of a certain L7 policy's rule."""
|
||||
return self.get(self.lbaas_l7rule_path % (l7policy, l7rule),
|
||||
params=_params)
|
||||
|
||||
@APIParamsCall
|
||||
def create_lbaas_l7rule(self, l7policy, body=None):
|
||||
"""Creates rule for a certain L7 policy."""
|
||||
return self.post(self.lbaas_l7rules_path % l7policy, body=body)
|
||||
|
||||
@APIParamsCall
|
||||
def update_lbaas_l7rule(self, l7rule, l7policy, body=None):
|
||||
"""Updates L7 rule."""
|
||||
return self.put(self.lbaas_l7rule_path % (l7policy, l7rule),
|
||||
body=body)
|
||||
|
||||
@APIParamsCall
|
||||
def delete_lbaas_l7rule(self, l7rule, l7policy):
|
||||
"""Deletes the specified L7 rule."""
|
||||
return self.delete(self.lbaas_l7rule_path % (l7policy, l7rule))
|
||||
|
||||
@APIParamsCall
|
||||
def list_lbaas_pools(self, retrieve_all=True, **_params):
|
||||
"""Fetches a list of all lbaas_pools for a tenant."""
|
||||
@@ -1338,6 +1529,30 @@ class Client(ClientBase):
|
||||
return self.post((self.agent_path + self.L3_ROUTERS) % l3_agent,
|
||||
body=body)
|
||||
|
||||
@APIParamsCall
|
||||
def list_dragents_hosting_bgp_speaker(self, bgp_speaker, **_params):
|
||||
"""Fetches a list of Dynamic Routing agents hosting a BGP speaker."""
|
||||
return self.get((self.bgp_speaker_path + self.BGP_DRAGENTS)
|
||||
% bgp_speaker, params=_params)
|
||||
|
||||
@APIParamsCall
|
||||
def add_bgp_speaker_to_dragent(self, bgp_dragent, body):
|
||||
"""Adds a BGP speaker to Dynamic Routing agent."""
|
||||
return self.post((self.agent_path + self.BGP_DRINSTANCES)
|
||||
% bgp_dragent, body=body)
|
||||
|
||||
@APIParamsCall
|
||||
def remove_bgp_speaker_from_dragent(self, bgp_dragent, bgpspeaker_id):
|
||||
"""Removes a BGP speaker from Dynamic Routing agent."""
|
||||
return self.delete((self.agent_path + self.BGP_DRINSTANCES + "/%s")
|
||||
% (bgp_dragent, bgpspeaker_id))
|
||||
|
||||
@APIParamsCall
|
||||
def list_bgp_speaker_on_dragent(self, bgp_dragent, **_params):
|
||||
"""Fetches a list of BGP speakers hosted by Dynamic Routing agent."""
|
||||
return self.get((self.agent_path + self.BGP_DRINSTANCES)
|
||||
% bgp_dragent, params=_params)
|
||||
|
||||
@APIParamsCall
|
||||
def list_firewall_rules(self, retrieve_all=True, **_params):
|
||||
"""Fetches a list of all firewall rules for a tenant."""
|
||||
@@ -1411,7 +1626,7 @@ class Client(ClientBase):
|
||||
|
||||
@APIParamsCall
|
||||
def list_firewalls(self, retrieve_all=True, **_params):
|
||||
"""Fetches a list of all firewals for a tenant."""
|
||||
"""Fetches a list of all firewalls for a tenant."""
|
||||
# Pass filters in "params" argument to do_request
|
||||
|
||||
return self.list('firewalls', self.firewalls_path, retrieve_all,
|
||||
@@ -1686,6 +1901,133 @@ class Client(ClientBase):
|
||||
return self.list('availability_zones', self.availability_zones_path,
|
||||
retrieve_all, **_params)
|
||||
|
||||
@APIParamsCall
|
||||
def get_auto_allocated_topology(self, tenant_id, **_params):
|
||||
"""Fetch information about a tenant's auto-allocated topology."""
|
||||
return self.get(
|
||||
self.auto_allocated_topology_path % tenant_id,
|
||||
params=_params)
|
||||
|
||||
def validate_auto_allocated_topology_requirements(self, tenant_id):
|
||||
"""Validate requirements for getting an auto-allocated topology."""
|
||||
return self.get_auto_allocated_topology(tenant_id, fields=['dry-run'])
|
||||
|
||||
@APIParamsCall
|
||||
def list_bgp_speakers(self, retrieve_all=True, **_params):
|
||||
"""Fetches a list of all BGP speakers for a tenant."""
|
||||
return self.list('bgp_speakers', self.bgp_speakers_path, retrieve_all,
|
||||
**_params)
|
||||
|
||||
@APIParamsCall
|
||||
def show_bgp_speaker(self, bgp_speaker_id, **_params):
|
||||
"""Fetches information of a certain BGP speaker."""
|
||||
return self.get(self.bgp_speaker_path % (bgp_speaker_id),
|
||||
params=_params)
|
||||
|
||||
@APIParamsCall
|
||||
def create_bgp_speaker(self, body=None):
|
||||
"""Creates a new BGP speaker."""
|
||||
return self.post(self.bgp_speakers_path, body=body)
|
||||
|
||||
@APIParamsCall
|
||||
def update_bgp_speaker(self, bgp_speaker_id, body=None):
|
||||
"""Update a BGP speaker."""
|
||||
return self.put(self.bgp_speaker_path % bgp_speaker_id, body=body)
|
||||
|
||||
@APIParamsCall
|
||||
def delete_bgp_speaker(self, speaker_id):
|
||||
"""Deletes the specified BGP speaker."""
|
||||
return self.delete(self.bgp_speaker_path % (speaker_id))
|
||||
|
||||
@APIParamsCall
|
||||
def add_peer_to_bgp_speaker(self, speaker_id, body=None):
|
||||
"""Adds a peer to BGP speaker."""
|
||||
return self.put((self.bgp_speaker_path % speaker_id) +
|
||||
"/add_bgp_peer", body=body)
|
||||
|
||||
@APIParamsCall
|
||||
def remove_peer_from_bgp_speaker(self, speaker_id, body=None):
|
||||
"""Removes a peer from BGP speaker."""
|
||||
return self.put((self.bgp_speaker_path % speaker_id) +
|
||||
"/remove_bgp_peer", body=body)
|
||||
|
||||
@APIParamsCall
|
||||
def add_network_to_bgp_speaker(self, speaker_id, body=None):
|
||||
"""Adds a network to BGP speaker."""
|
||||
return self.put((self.bgp_speaker_path % speaker_id) +
|
||||
"/add_gateway_network", body=body)
|
||||
|
||||
@APIParamsCall
|
||||
def remove_network_from_bgp_speaker(self, speaker_id, body=None):
|
||||
"""Removes a network from BGP speaker."""
|
||||
return self.put((self.bgp_speaker_path % speaker_id) +
|
||||
"/remove_gateway_network", body=body)
|
||||
|
||||
@APIParamsCall
|
||||
def list_route_advertised_from_bgp_speaker(self, speaker_id, **_params):
|
||||
"""Fetches a list of all routes advertised by BGP speaker."""
|
||||
return self.get((self.bgp_speaker_path % speaker_id) +
|
||||
"/get_advertised_routes", params=_params)
|
||||
|
||||
@APIParamsCall
|
||||
def list_bgp_peers(self, **_params):
|
||||
"""Fetches a list of all BGP peers."""
|
||||
return self.get(self.bgp_peers_path, params=_params)
|
||||
|
||||
@APIParamsCall
|
||||
def show_bgp_peer(self, peer_id, **_params):
|
||||
"""Fetches information of a certain BGP peer."""
|
||||
return self.get(self.bgp_peer_path % peer_id,
|
||||
params=_params)
|
||||
|
||||
@APIParamsCall
|
||||
def create_bgp_peer(self, body=None):
|
||||
"""Create a new BGP peer."""
|
||||
return self.post(self.bgp_peers_path, body=body)
|
||||
|
||||
@APIParamsCall
|
||||
def update_bgp_peer(self, bgp_peer_id, body=None):
|
||||
"""Update a BGP peer."""
|
||||
return self.put(self.bgp_peer_path % bgp_peer_id, body=body)
|
||||
|
||||
@APIParamsCall
|
||||
def delete_bgp_peer(self, peer_id):
|
||||
"""Deletes the specified BGP peer."""
|
||||
return self.delete(self.bgp_peer_path % peer_id)
|
||||
|
||||
@APIParamsCall
|
||||
def list_network_ip_availabilities(self, retrieve_all=True, **_params):
|
||||
"""Fetches IP availibility information for all networks"""
|
||||
return self.list('network_ip_availabilities',
|
||||
self.network_ip_availabilities_path,
|
||||
retrieve_all, **_params)
|
||||
|
||||
@APIParamsCall
|
||||
def show_network_ip_availability(self, network, **_params):
|
||||
"""Fetches IP availability information for a specified network"""
|
||||
return self.get(self.network_ip_availability_path % (network),
|
||||
params=_params)
|
||||
|
||||
@APIParamsCall
|
||||
def add_tag(self, resource_type, resource_id, tag, **_params):
|
||||
"""Add a tag on the resource."""
|
||||
return self.put(self.tag_path % (resource_type, resource_id, tag))
|
||||
|
||||
@APIParamsCall
|
||||
def replace_tag(self, resource_type, resource_id, body, **_params):
|
||||
"""Replace tags on the resource."""
|
||||
return self.put(self.tags_path % (resource_type, resource_id), body)
|
||||
|
||||
@APIParamsCall
|
||||
def remove_tag(self, resource_type, resource_id, tag, **_params):
|
||||
"""Remove a tag on the resource."""
|
||||
return self.delete(self.tag_path % (resource_type, resource_id, tag))
|
||||
|
||||
@APIParamsCall
|
||||
def remove_tag_all(self, resource_type, resource_id, **_params):
|
||||
"""Remove all tags on the resource."""
|
||||
return self.delete(self.tags_path % (resource_type, resource_id))
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
"""Initialize a new client for the Neutron v2.0 API."""
|
||||
super(Client, self).__init__(**kwargs)
|
||||
@@ -1701,11 +2043,13 @@ class Client(ClientBase):
|
||||
setattr(self, "show_%s" % resource_singular, fn)
|
||||
|
||||
def extend_list(self, resource_plural, path, parent_resource):
|
||||
def _fx(**_params):
|
||||
return self.list_ext(path, **_params)
|
||||
def _fx(retrieve_all=True, **_params):
|
||||
return self.list_ext(resource_plural, path,
|
||||
retrieve_all, **_params)
|
||||
|
||||
def _parent_fx(parent_id, **_params):
|
||||
return self.list_ext(path % parent_id, **_params)
|
||||
def _parent_fx(parent_id, retrieve_all=True, **_params):
|
||||
return self.list_ext(resource_plural, path % parent_id,
|
||||
retrieve_all, **_params)
|
||||
fn = _fx if not parent_resource else _parent_fx
|
||||
setattr(self, "list_%s" % resource_plural, fn)
|
||||
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
---
|
||||
features:
|
||||
- |
|
||||
CLI support for the "get-me-a-network" feature, which simplifies
|
||||
the process for launching an instance with basic network
|
||||
connectivity.
|
||||
|
||||
* The ``auto-allocated-topology-show`` command provides the
|
||||
network of the automatically allocated topology for a tenant.
|
||||
@@ -0,0 +1,8 @@
|
||||
---
|
||||
features:
|
||||
- |
|
||||
CLI support for Layer 7 content policies and rules.
|
||||
|
||||
* L7 policies can be defined for listeners along
|
||||
with the ability to set L7 policy order.
|
||||
* Multiple rules can be created for an L7 policy.
|
||||
@@ -0,0 +1,7 @@
|
||||
---
|
||||
features:
|
||||
- |
|
||||
CLI support for load balancer status tree.
|
||||
|
||||
* The ``lbaas-loadbalancer-status`` command provides the status
|
||||
tree of a specific load balancer.
|
||||
16
releasenotes/notes/add-neutron-purge-a89e3d1179dce4b1.yaml
Normal file
16
releasenotes/notes/add-neutron-purge-a89e3d1179dce4b1.yaml
Normal file
@@ -0,0 +1,16 @@
|
||||
---
|
||||
features:
|
||||
- |
|
||||
New command 'neutron purge <tenant_id>' will delete all
|
||||
supported resources owned by the given tenant, provided
|
||||
that the user has sufficient authorization and the
|
||||
resources in question are not shared, in use, or
|
||||
otherwise undeletable.
|
||||
|
||||
Supported resources are:
|
||||
* Networks
|
||||
* Subnets
|
||||
* Routers
|
||||
* Ports
|
||||
* Floating IPs
|
||||
* Security Groups
|
||||
@@ -0,0 +1,8 @@
|
||||
---
|
||||
features:
|
||||
- |
|
||||
CLI support for QoS policy RBAC.
|
||||
|
||||
* The ``rbac-create`` command include a --type qos-policy
|
||||
option.
|
||||
* The ``rbac-list`` command output includes a new 'type' column.
|
||||
@@ -0,0 +1,8 @@
|
||||
---
|
||||
features:
|
||||
- |
|
||||
CLI support for Neutron-LBaaS v2 shared pools added.
|
||||
|
||||
* Pools can be created independently from listeners.
|
||||
* Listeners can share the same default_pool.
|
||||
* Makes Layer 7 switching support much more useful.
|
||||
16
releasenotes/notes/add-tag-support-bad62d60ecc7075c.yaml
Normal file
16
releasenotes/notes/add-tag-support-bad62d60ecc7075c.yaml
Normal file
@@ -0,0 +1,16 @@
|
||||
---
|
||||
features:
|
||||
- |
|
||||
CLI support for tag.
|
||||
|
||||
* The ``tag-add`` command sets a tag on the network resource. It also
|
||||
includes ``--resource-type``, ``--resource`` and ``--tag`` options.
|
||||
* The ``tag-replace`` command replaces tags on the network resource. It
|
||||
also includes ``--resource-type``, ``--resource`` and ``--tag``
|
||||
options. More than one ``--tag`` options can be set.
|
||||
* The ``tag-remove`` command removes tags on the network resource. It also
|
||||
includes ``--resource-type``, ``--resource``, ``--tag`` and ``--all``
|
||||
options. The ``--all`` option allow to remove all tags on the network
|
||||
resource.
|
||||
* The ``net-list`` command includes ``--tags``, ``--tags-any``,
|
||||
``--not-tags`` and ``--not-tags-any`` options.
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
features:
|
||||
- |
|
||||
CLI support for the BGP dynamic routing functionality will help
|
||||
advertising neutron fixed-ips and dvr host routes via BGP.
|
||||
@@ -0,0 +1,9 @@
|
||||
---
|
||||
features:
|
||||
- |
|
||||
CLI support for default subnetpools.
|
||||
|
||||
* The ``subnetpool-list`` and ``subnetpool-show`` command output includes
|
||||
the ``is_default`` field.
|
||||
* The ``subnetpool-create`` and ``subnetpool-update`` commands include a
|
||||
``--is-default`` option.
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
critical:
|
||||
- Fix a critical bug that when lazy translation is enabled
|
||||
NeutronClientException raises a TypeError
|
||||
(`bug 1552760 <https://bugs.launchpad.net/heat/+bug/1552760>`_).
|
||||
@@ -0,0 +1,6 @@
|
||||
---
|
||||
fixes:
|
||||
- Fix `bug 1450414 <https://bugs.launchpad.net/python-neutronclient/+bug/1450414>`_
|
||||
that authentication with via ``--os-token`` and ``--os-url`` options
|
||||
(or corresponding environment variables) does not work
|
||||
after keystone v3 API support.
|
||||
@@ -0,0 +1,9 @@
|
||||
---
|
||||
features:
|
||||
- |
|
||||
CLI support for network IP availability
|
||||
|
||||
* The ``net-ip-availability-list`` command provides a list of IP
|
||||
usage statistics for all networks.
|
||||
* The ``net-ip-availability-show`` command provides IP usage stats
|
||||
for a specific network.
|
||||
@@ -16,6 +16,7 @@ upgrade:
|
||||
- Cisco-specific neutron client commands have been removed.
|
||||
These commands are ported to networking-cisco.
|
||||
- py26 support has been dropped.
|
||||
- py33 support has been dropped.
|
||||
fixes:
|
||||
- Name is no longer looked up on RBAC policies,
|
||||
RBAC policies have no name field so the name query to
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
---
|
||||
features:
|
||||
- Neutron client returns 'x-openstack-request-id'.
|
||||
@@ -10,6 +10,7 @@ Old 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
|
||||
* add ability to retrieve a specific load balancer's status tree
|
||||
|
||||
2.2.0
|
||||
-----
|
||||
|
||||
@@ -1,18 +1,17 @@
|
||||
# The order of packages is significant, because pip processes them in the order
|
||||
# of appearance. Changing the order has an impact on the overall integration
|
||||
# process, which may cause wedges in the gate later.
|
||||
pbr>=1.6
|
||||
argparse
|
||||
cliff>=1.15.0 # Apache-2.0
|
||||
debtcollector>=0.3.0 # Apache-2.0
|
||||
iso8601>=0.1.9
|
||||
netaddr!=0.7.16,>=0.7.12
|
||||
oslo.i18n>=1.5.0 # Apache-2.0
|
||||
pbr>=1.6 # Apache-2.0
|
||||
cliff!=1.16.0,>=1.15.0 # Apache-2.0
|
||||
debtcollector>=1.2.0 # Apache-2.0
|
||||
iso8601>=0.1.9 # MIT
|
||||
netaddr!=0.7.16,>=0.7.12 # BSD
|
||||
oslo.i18n>=2.1.0 # Apache-2.0
|
||||
oslo.serialization>=1.10.0 # Apache-2.0
|
||||
oslo.utils>=3.2.0 # Apache-2.0
|
||||
os-client-config>=1.13.1
|
||||
keystoneauth1>=2.1.0
|
||||
requests!=2.9.0,>=2.8.1
|
||||
simplejson>=2.2.0
|
||||
six>=1.9.0
|
||||
Babel>=1.3
|
||||
oslo.utils>=3.5.0 # Apache-2.0
|
||||
os-client-config>=1.13.1 # Apache-2.0
|
||||
keystoneauth1>=2.1.0 # Apache-2.0
|
||||
requests!=2.9.0,>=2.8.1 # Apache-2.0
|
||||
simplejson>=2.2.0 # MIT
|
||||
six>=1.9.0 # MIT
|
||||
Babel>=1.3 # BSD
|
||||
|
||||
14
setup.cfg
14
setup.cfg
@@ -38,3 +38,17 @@ source-dir = doc/source
|
||||
|
||||
[wheel]
|
||||
universal = 1
|
||||
|
||||
[extract_messages]
|
||||
keywords = _ gettext ngettext l_ lazy_gettext
|
||||
mapping_file = babel.cfg
|
||||
output_file = neutronclient/locale/neutronclient.pot
|
||||
|
||||
[compile_catalog]
|
||||
directory = neutronclient/locale
|
||||
domain = neutronclient
|
||||
|
||||
[update_catalog]
|
||||
domain = neutronclient
|
||||
output_dir = neutronclient/locale
|
||||
input_file = neutronclient/locale/neutronclient.pot
|
||||
|
||||
@@ -3,17 +3,18 @@
|
||||
# process, which may cause wedges in the gate later.
|
||||
hacking<0.11,>=0.10.0
|
||||
|
||||
coverage>=3.6
|
||||
discover
|
||||
fixtures>=1.3.1
|
||||
mox3>=0.7.0
|
||||
mock>=1.2
|
||||
coverage>=3.6 # Apache-2.0
|
||||
discover # BSD
|
||||
fixtures>=1.3.1 # Apache-2.0/BSD
|
||||
mox3>=0.7.0 # Apache-2.0
|
||||
mock>=1.2 # BSD
|
||||
oslosphinx!=3.4.0,>=2.5.0 # Apache-2.0
|
||||
oslotest>=1.10.0 # Apache-2.0
|
||||
python-subunit>=0.0.18
|
||||
python-subunit>=0.0.18 # Apache-2.0/BSD
|
||||
reno>=0.1.1 # Apache2
|
||||
requests-mock>=0.7.0 # Apache-2.0
|
||||
sphinx!=1.2.0,!=1.3b1,<1.3,>=1.1.2
|
||||
testrepository>=0.0.18
|
||||
testtools>=1.4.0
|
||||
tempest-lib>=0.12.0
|
||||
sphinx!=1.2.0,!=1.3b1,<1.3,>=1.1.2 # BSD
|
||||
testrepository>=0.0.18 # Apache-2.0/BSD
|
||||
testtools>=1.4.0 # MIT
|
||||
testscenarios>=0.4 # Apache-2.0/BSD
|
||||
tempest-lib>=0.14.0 # Apache-2.0
|
||||
|
||||
9
tox.ini
9
tox.ini
@@ -13,7 +13,14 @@ usedevelop = True
|
||||
install_command = pip install -U {opts} {packages}
|
||||
deps = -r{toxinidir}/requirements.txt
|
||||
-r{toxinidir}/test-requirements.txt
|
||||
commands = python setup.py testr --testr-args='{posargs}'
|
||||
# Delete bytecodes from normal directories before running tests.
|
||||
# Note that bytecodes in dot directories will not be deleted
|
||||
# to keep bytecodes of python modules installed into virtualenvs.
|
||||
commands = sh -c "find . -type d -name '.?*' -prune -o \
|
||||
\( -type d -name '__pycache__' -o -type f -name '*.py[co]' \) \
|
||||
-print0 | xargs -0 rm -rf"
|
||||
python setup.py testr --testr-args='{posargs}'
|
||||
whitelist_externals = sh
|
||||
|
||||
[testenv:pep8]
|
||||
commands = flake8
|
||||
|
||||
Reference in New Issue
Block a user