GBP Client for GBP resources
Partially-implements: blueprint group-based-policy-abstraction Change-Id: I6925ab7e3cdbce741f7c3f73c4e810d7ca8b5c7a
This commit is contained in:
parent
a11f06cd4a
commit
e4f3993e2f
1
.gitignore
vendored
1
.gitignore
vendored
@ -14,6 +14,7 @@ gbp/vcsversion.py
|
|||||||
gbpclient/versioninfo
|
gbpclient/versioninfo
|
||||||
run_tests.err.log
|
run_tests.err.log
|
||||||
run_tests.log
|
run_tests.log
|
||||||
|
subunit.log
|
||||||
.autogenerated
|
.autogenerated
|
||||||
.coverage
|
.coverage
|
||||||
.testrepository/
|
.testrepository/
|
||||||
|
26
HACKING.rst
Normal file
26
HACKING.rst
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
Neutron Style Commandments
|
||||||
|
================================
|
||||||
|
|
||||||
|
- Step 1: Read the OpenStack Style Commandments
|
||||||
|
http://docs.openstack.org/developer/hacking/
|
||||||
|
- Step 2: Read on
|
||||||
|
|
||||||
|
|
||||||
|
Running Tests
|
||||||
|
-------------
|
||||||
|
The testing system is based on a combination of tox and testr. The canonical
|
||||||
|
approach to running tests is to simply run the command `tox`. This will
|
||||||
|
create virtual environments, populate them with depenedencies and run all of
|
||||||
|
the tests that OpenStack CI systems run. Behind the scenes, tox is running
|
||||||
|
`testr run --parallel`, but is set up such that you can supply any additional
|
||||||
|
testr arguments that are needed to tox. For example, you can run:
|
||||||
|
`tox -- --analyze-isolation` to cause tox to tell testr to add
|
||||||
|
--analyze-isolation to its argument list.
|
||||||
|
|
||||||
|
It is also possible to run the tests inside of a virtual environment
|
||||||
|
you have created, or it is possible that you have all of the dependencies
|
||||||
|
installed locally already. In this case, you can interact with the testr
|
||||||
|
command directly. Running `testr run` will run the entire test suite. `testr
|
||||||
|
run --parallel` will run it in parallel (this is the default incantation tox
|
||||||
|
uses.) More information about testr can be found at:
|
||||||
|
http://wiki.openstack.org/testr
|
176
LICENSE
Normal file
176
LICENSE
Normal file
@ -0,0 +1,176 @@
|
|||||||
|
|
||||||
|
Apache License
|
||||||
|
Version 2.0, January 2004
|
||||||
|
http://www.apache.org/licenses/
|
||||||
|
|
||||||
|
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||||
|
|
||||||
|
1. Definitions.
|
||||||
|
|
||||||
|
"License" shall mean the terms and conditions for use, reproduction,
|
||||||
|
and distribution as defined by Sections 1 through 9 of this document.
|
||||||
|
|
||||||
|
"Licensor" shall mean the copyright owner or entity authorized by
|
||||||
|
the copyright owner that is granting the License.
|
||||||
|
|
||||||
|
"Legal Entity" shall mean the union of the acting entity and all
|
||||||
|
other entities that control, are controlled by, or are under common
|
||||||
|
control with that entity. For the purposes of this definition,
|
||||||
|
"control" means (i) the power, direct or indirect, to cause the
|
||||||
|
direction or management of such entity, whether by contract or
|
||||||
|
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||||
|
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||||
|
|
||||||
|
"You" (or "Your") shall mean an individual or Legal Entity
|
||||||
|
exercising permissions granted by this License.
|
||||||
|
|
||||||
|
"Source" form shall mean the preferred form for making modifications,
|
||||||
|
including but not limited to software source code, documentation
|
||||||
|
source, and configuration files.
|
||||||
|
|
||||||
|
"Object" form shall mean any form resulting from mechanical
|
||||||
|
transformation or translation of a Source form, including but
|
||||||
|
not limited to compiled object code, generated documentation,
|
||||||
|
and conversions to other media types.
|
||||||
|
|
||||||
|
"Work" shall mean the work of authorship, whether in Source or
|
||||||
|
Object form, made available under the License, as indicated by a
|
||||||
|
copyright notice that is included in or attached to the work
|
||||||
|
(an example is provided in the Appendix below).
|
||||||
|
|
||||||
|
"Derivative Works" shall mean any work, whether in Source or Object
|
||||||
|
form, that is based on (or derived from) the Work and for which the
|
||||||
|
editorial revisions, annotations, elaborations, or other modifications
|
||||||
|
represent, as a whole, an original work of authorship. For the purposes
|
||||||
|
of this License, Derivative Works shall not include works that remain
|
||||||
|
separable from, or merely link (or bind by name) to the interfaces of,
|
||||||
|
the Work and Derivative Works thereof.
|
||||||
|
|
||||||
|
"Contribution" shall mean any work of authorship, including
|
||||||
|
the original version of the Work and any modifications or additions
|
||||||
|
to that Work or Derivative Works thereof, that is intentionally
|
||||||
|
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||||
|
or by an individual or Legal Entity authorized to submit on behalf of
|
||||||
|
the copyright owner. For the purposes of this definition, "submitted"
|
||||||
|
means any form of electronic, verbal, or written communication sent
|
||||||
|
to the Licensor or its representatives, including but not limited to
|
||||||
|
communication on electronic mailing lists, source code control systems,
|
||||||
|
and issue tracking systems that are managed by, or on behalf of, the
|
||||||
|
Licensor for the purpose of discussing and improving the Work, but
|
||||||
|
excluding communication that is conspicuously marked or otherwise
|
||||||
|
designated in writing by the copyright owner as "Not a Contribution."
|
||||||
|
|
||||||
|
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||||
|
on behalf of whom a Contribution has been received by Licensor and
|
||||||
|
subsequently incorporated within the Work.
|
||||||
|
|
||||||
|
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||||
|
this License, each Contributor hereby grants to You a perpetual,
|
||||||
|
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||||
|
copyright license to reproduce, prepare Derivative Works of,
|
||||||
|
publicly display, publicly perform, sublicense, and distribute the
|
||||||
|
Work and such Derivative Works in Source or Object form.
|
||||||
|
|
||||||
|
3. Grant of Patent License. Subject to the terms and conditions of
|
||||||
|
this License, each Contributor hereby grants to You a perpetual,
|
||||||
|
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||||
|
(except as stated in this section) patent license to make, have made,
|
||||||
|
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||||
|
where such license applies only to those patent claims licensable
|
||||||
|
by such Contributor that are necessarily infringed by their
|
||||||
|
Contribution(s) alone or by combination of their Contribution(s)
|
||||||
|
with the Work to which such Contribution(s) was submitted. If You
|
||||||
|
institute patent litigation against any entity (including a
|
||||||
|
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||||
|
or a Contribution incorporated within the Work constitutes direct
|
||||||
|
or contributory patent infringement, then any patent licenses
|
||||||
|
granted to You under this License for that Work shall terminate
|
||||||
|
as of the date such litigation is filed.
|
||||||
|
|
||||||
|
4. Redistribution. You may reproduce and distribute copies of the
|
||||||
|
Work or Derivative Works thereof in any medium, with or without
|
||||||
|
modifications, and in Source or Object form, provided that You
|
||||||
|
meet the following conditions:
|
||||||
|
|
||||||
|
(a) You must give any other recipients of the Work or
|
||||||
|
Derivative Works a copy of this License; and
|
||||||
|
|
||||||
|
(b) You must cause any modified files to carry prominent notices
|
||||||
|
stating that You changed the files; and
|
||||||
|
|
||||||
|
(c) You must retain, in the Source form of any Derivative Works
|
||||||
|
that You distribute, all copyright, patent, trademark, and
|
||||||
|
attribution notices from the Source form of the Work,
|
||||||
|
excluding those notices that do not pertain to any part of
|
||||||
|
the Derivative Works; and
|
||||||
|
|
||||||
|
(d) If the Work includes a "NOTICE" text file as part of its
|
||||||
|
distribution, then any Derivative Works that You distribute must
|
||||||
|
include a readable copy of the attribution notices contained
|
||||||
|
within such NOTICE file, excluding those notices that do not
|
||||||
|
pertain to any part of the Derivative Works, in at least one
|
||||||
|
of the following places: within a NOTICE text file distributed
|
||||||
|
as part of the Derivative Works; within the Source form or
|
||||||
|
documentation, if provided along with the Derivative Works; or,
|
||||||
|
within a display generated by the Derivative Works, if and
|
||||||
|
wherever such third-party notices normally appear. The contents
|
||||||
|
of the NOTICE file are for informational purposes only and
|
||||||
|
do not modify the License. You may add Your own attribution
|
||||||
|
notices within Derivative Works that You distribute, alongside
|
||||||
|
or as an addendum to the NOTICE text from the Work, provided
|
||||||
|
that such additional attribution notices cannot be construed
|
||||||
|
as modifying the License.
|
||||||
|
|
||||||
|
You may add Your own copyright statement to Your modifications and
|
||||||
|
may provide additional or different license terms and conditions
|
||||||
|
for use, reproduction, or distribution of Your modifications, or
|
||||||
|
for any such Derivative Works as a whole, provided Your use,
|
||||||
|
reproduction, and distribution of the Work otherwise complies with
|
||||||
|
the conditions stated in this License.
|
||||||
|
|
||||||
|
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||||
|
any Contribution intentionally submitted for inclusion in the Work
|
||||||
|
by You to the Licensor shall be under the terms and conditions of
|
||||||
|
this License, without any additional terms or conditions.
|
||||||
|
Notwithstanding the above, nothing herein shall supersede or modify
|
||||||
|
the terms of any separate license agreement you may have executed
|
||||||
|
with Licensor regarding such Contributions.
|
||||||
|
|
||||||
|
6. Trademarks. This License does not grant permission to use the trade
|
||||||
|
names, trademarks, service marks, or product names of the Licensor,
|
||||||
|
except as required for reasonable and customary use in describing the
|
||||||
|
origin of the Work and reproducing the content of the NOTICE file.
|
||||||
|
|
||||||
|
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||||
|
agreed to in writing, Licensor provides the Work (and each
|
||||||
|
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||||
|
implied, including, without limitation, any warranties or conditions
|
||||||
|
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||||
|
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||||
|
appropriateness of using or redistributing the Work and assume any
|
||||||
|
risks associated with Your exercise of permissions under this License.
|
||||||
|
|
||||||
|
8. Limitation of Liability. In no event and under no legal theory,
|
||||||
|
whether in tort (including negligence), contract, or otherwise,
|
||||||
|
unless required by applicable law (such as deliberate and grossly
|
||||||
|
negligent acts) or agreed to in writing, shall any Contributor be
|
||||||
|
liable to You for damages, including any direct, indirect, special,
|
||||||
|
incidental, or consequential damages of any character arising as a
|
||||||
|
result of this License or out of the use or inability to use the
|
||||||
|
Work (including but not limited to damages for loss of goodwill,
|
||||||
|
work stoppage, computer failure or malfunction, or any and all
|
||||||
|
other commercial damages or losses), even if such Contributor
|
||||||
|
has been advised of the possibility of such damages.
|
||||||
|
|
||||||
|
9. Accepting Warranty or Additional Liability. While redistributing
|
||||||
|
the Work or Derivative Works thereof, You may choose to offer,
|
||||||
|
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||||
|
or other liability obligations and/or rights consistent with this
|
||||||
|
License. However, in accepting such obligations, You may act only
|
||||||
|
on Your own behalf and on Your sole responsibility, not on behalf
|
||||||
|
of any other Contributor, and only if You agree to indemnify,
|
||||||
|
defend, and hold each Contributor harmless for any liability
|
||||||
|
incurred by, or claims asserted against, such Contributor by reason
|
||||||
|
of your accepting any such warranty or additional liability.
|
||||||
|
|
0
gbpclient/gbp/__init__.py
Normal file
0
gbpclient/gbp/__init__.py
Normal file
3
gbpclient/gbp/v2_0/__init__.py
Normal file
3
gbpclient/gbp/v2_0/__init__.py
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
from neutronclient.neutron import v2_0 as neutronV2_0
|
||||||
|
|
||||||
|
_get_resource_plural = neutronV2_0._get_resource_plural
|
861
gbpclient/gbp/v2_0/groupbasedpolicy.py
Normal file
861
gbpclient/gbp/v2_0/groupbasedpolicy.py
Normal file
@ -0,0 +1,861 @@
|
|||||||
|
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||||
|
# not use this file except in compliance with the License. You may obtain
|
||||||
|
# a copy of the License at
|
||||||
|
#
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||||
|
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||||
|
# License for the specific language governing permissions and limitations
|
||||||
|
# under the License.
|
||||||
|
#
|
||||||
|
|
||||||
|
import logging
|
||||||
|
import string
|
||||||
|
|
||||||
|
from neutronclient.common import utils
|
||||||
|
from neutronclient.neutron import v2_0 as neutronV20
|
||||||
|
from neutronclient.openstack.common.gettextutils import _
|
||||||
|
from oslo.serialization import jsonutils
|
||||||
|
|
||||||
|
|
||||||
|
def _format_network_service_params(net_svc_policy):
|
||||||
|
try:
|
||||||
|
return '\n'.join([jsonutils.dumps(param) for param in
|
||||||
|
net_svc_policy['network_service_params']])
|
||||||
|
except (TypeError, KeyError):
|
||||||
|
return ''
|
||||||
|
|
||||||
|
|
||||||
|
class ListEndpoint(neutronV20.ListCommand):
|
||||||
|
"""List policy_targets that belong to a given tenant."""
|
||||||
|
|
||||||
|
resource = 'endpoint'
|
||||||
|
log = logging.getLogger(__name__ + '.ListEndpoint')
|
||||||
|
_formatters = {}
|
||||||
|
list_columns = ['id', 'name', 'description', 'endpoint_group_id']
|
||||||
|
pagination_support = True
|
||||||
|
sorting_support = True
|
||||||
|
|
||||||
|
|
||||||
|
class ShowEndpoint(neutronV20.ShowCommand):
|
||||||
|
"""Show information of a given policy_target."""
|
||||||
|
|
||||||
|
resource = 'endpoint'
|
||||||
|
log = logging.getLogger(__name__ + '.ShowEndpoint')
|
||||||
|
|
||||||
|
|
||||||
|
class CreateEndpoint(neutronV20.CreateCommand):
|
||||||
|
"""Create a policy_target for a given tenant."""
|
||||||
|
|
||||||
|
resource = 'endpoint'
|
||||||
|
log = logging.getLogger(__name__ + '.CreateEndpoint')
|
||||||
|
|
||||||
|
def add_known_arguments(self, parser):
|
||||||
|
parser.add_argument(
|
||||||
|
'--description',
|
||||||
|
help=_('Description of the policy_target'))
|
||||||
|
parser.add_argument(
|
||||||
|
'--endpoint-group', metavar='EPG',
|
||||||
|
default='',
|
||||||
|
help=_('group uuid'))
|
||||||
|
parser.add_argument(
|
||||||
|
'--port',
|
||||||
|
default='',
|
||||||
|
help=_('Neutron Port'))
|
||||||
|
parser.add_argument(
|
||||||
|
'name', metavar='NAME',
|
||||||
|
help=_('Name of policy_target to create'))
|
||||||
|
|
||||||
|
def args2body(self, parsed_args):
|
||||||
|
body = {self.resource: {}, }
|
||||||
|
|
||||||
|
neutronV20.update_dict(parsed_args, body[self.resource],
|
||||||
|
['name', 'tenant_id', 'description'])
|
||||||
|
if parsed_args.endpoint_group:
|
||||||
|
body[self.resource]['endpoint_group_id'] = \
|
||||||
|
neutronV20.find_resourceid_by_name_or_id(
|
||||||
|
self.get_client(), 'endpoint_group',
|
||||||
|
parsed_args.endpoint_group)
|
||||||
|
if parsed_args.port:
|
||||||
|
body[self.resource]['port_id'] = \
|
||||||
|
neutronV20.find_resourceid_by_name_or_id(
|
||||||
|
self.get_client(), 'port',
|
||||||
|
parsed_args.port)
|
||||||
|
return body
|
||||||
|
|
||||||
|
|
||||||
|
class DeleteEndpoint(neutronV20.DeleteCommand):
|
||||||
|
"""Delete a given policy_target."""
|
||||||
|
|
||||||
|
resource = 'endpoint'
|
||||||
|
log = logging.getLogger(__name__ + '.DeleteEndpoint')
|
||||||
|
|
||||||
|
|
||||||
|
class UpdateEndpoint(neutronV20.UpdateCommand):
|
||||||
|
"""Update policy_target's information."""
|
||||||
|
|
||||||
|
resource = 'endpoint'
|
||||||
|
log = logging.getLogger(__name__ + '.UpdateEndpoint')
|
||||||
|
|
||||||
|
|
||||||
|
class ListEndpointGroup(neutronV20.ListCommand):
|
||||||
|
"""List groups that belong to a given tenant."""
|
||||||
|
|
||||||
|
resource = 'endpoint_group'
|
||||||
|
log = logging.getLogger(__name__ + '.ListEndpointGroup')
|
||||||
|
list_columns = ['id', 'name', 'description']
|
||||||
|
pagination_support = True
|
||||||
|
sorting_support = True
|
||||||
|
|
||||||
|
|
||||||
|
class ShowEndpointGroup(neutronV20.ShowCommand):
|
||||||
|
"""Show information of a given group."""
|
||||||
|
|
||||||
|
resource = 'endpoint_group'
|
||||||
|
log = logging.getLogger(__name__ + '.ShowEndpointGroup')
|
||||||
|
|
||||||
|
|
||||||
|
class CreateEndpointGroup(neutronV20.CreateCommand):
|
||||||
|
"""Create a group for a given tenant."""
|
||||||
|
|
||||||
|
resource = 'endpoint_group'
|
||||||
|
log = logging.getLogger(__name__ + '.CreateEndpointGroup')
|
||||||
|
|
||||||
|
def add_known_arguments(self, parser):
|
||||||
|
parser.add_argument(
|
||||||
|
'--description',
|
||||||
|
help=_('Description of the group'))
|
||||||
|
parser.add_argument(
|
||||||
|
'name', metavar='NAME',
|
||||||
|
help=_('Name of group to create'))
|
||||||
|
parser.add_argument(
|
||||||
|
'--l2-policy', metavar='L2_POLICY',
|
||||||
|
default='',
|
||||||
|
help=_('L2 policy uuid'))
|
||||||
|
parser.add_argument(
|
||||||
|
'--provided-contracts', type=utils.str2dict,
|
||||||
|
default={},
|
||||||
|
help=_('Dictionary of provided contract uuids'))
|
||||||
|
parser.add_argument(
|
||||||
|
'--consumed-contracts', type=utils.str2dict,
|
||||||
|
default={},
|
||||||
|
help=_('Dictionary of consumed contract uuids'))
|
||||||
|
parser.add_argument(
|
||||||
|
'--network-service-policy', metavar='NETWORK_SERVICE_POLICY',
|
||||||
|
default='',
|
||||||
|
help=_('Network service policy uuid'))
|
||||||
|
parser.add_argument(
|
||||||
|
'--subnets', type=string.split,
|
||||||
|
help=_('Subnet to map the group'))
|
||||||
|
|
||||||
|
def args2body(self, parsed_args):
|
||||||
|
body = {self.resource: {}, }
|
||||||
|
|
||||||
|
if parsed_args.l2_policy:
|
||||||
|
body[self.resource]['l2_policy_id'] = \
|
||||||
|
neutronV20.find_resourceid_by_name_or_id(
|
||||||
|
self.get_client(), 'l2_policy',
|
||||||
|
parsed_args.l2_policy)
|
||||||
|
|
||||||
|
if parsed_args.network_service_policy:
|
||||||
|
body[self.resource]['network_service_policy_id'] = \
|
||||||
|
neutronV20.find_resourceid_by_name_or_id(
|
||||||
|
self.get_client(), 'network_service_policy',
|
||||||
|
parsed_args.network_service_policy)
|
||||||
|
|
||||||
|
if parsed_args.provided_contracts:
|
||||||
|
for key in parsed_args.provided_contracts.keys():
|
||||||
|
id_key = neutronV20.find_resourceid_by_name_or_id(
|
||||||
|
self.get_client(), 'contract',
|
||||||
|
key)
|
||||||
|
parsed_args.provided_contracts[id_key] = \
|
||||||
|
parsed_args.provided_contracts.pop(key)
|
||||||
|
|
||||||
|
if parsed_args.consumed_contracts:
|
||||||
|
for key in parsed_args.consumed_contracts.keys():
|
||||||
|
id_key = neutronV20.find_resourceid_by_name_or_id(
|
||||||
|
self.get_client(), 'contract',
|
||||||
|
key)
|
||||||
|
parsed_args.consumed_contracts[id_key] = \
|
||||||
|
parsed_args.consumed_contracts.pop(key)
|
||||||
|
|
||||||
|
if parsed_args.subnets:
|
||||||
|
for subnet in parsed_args.subnets:
|
||||||
|
subnet_id = neutronV20.find_resourceid_by_name_or_id(
|
||||||
|
self.get_client(), 'subnet',
|
||||||
|
subnet)
|
||||||
|
parsed_args.subnets.remove(subnet)
|
||||||
|
parsed_args.subnets.append(subnet_id)
|
||||||
|
neutronV20.update_dict(parsed_args, body[self.resource],
|
||||||
|
['name', 'tenant_id', 'description',
|
||||||
|
'provided_contracts', 'subnets',
|
||||||
|
'consumed_contracts'])
|
||||||
|
|
||||||
|
return body
|
||||||
|
|
||||||
|
|
||||||
|
class DeleteEndpointGroup(neutronV20.DeleteCommand):
|
||||||
|
"""Delete a given group."""
|
||||||
|
|
||||||
|
resource = 'endpoint_group'
|
||||||
|
log = logging.getLogger(__name__ + '.DeleteEndpointGroup')
|
||||||
|
|
||||||
|
|
||||||
|
class UpdateEndpointGroup(neutronV20.UpdateCommand):
|
||||||
|
"""Update group's information."""
|
||||||
|
|
||||||
|
resource = 'endpoint_group'
|
||||||
|
log = logging.getLogger(__name__ + '.UpdateEndpointGroup')
|
||||||
|
|
||||||
|
def add_known_arguments(self, parser):
|
||||||
|
parser.add_argument(
|
||||||
|
'--description',
|
||||||
|
help=_('Description of the group'))
|
||||||
|
parser.add_argument(
|
||||||
|
'--l2-policy', metavar='L2_POLICY',
|
||||||
|
help=_('L2 policy uuid'))
|
||||||
|
parser.add_argument(
|
||||||
|
'--network-service-policy', metavar='NETWORK_SERVICE_POLICY',
|
||||||
|
help=_('Network Service Policy uuid'))
|
||||||
|
parser.add_argument(
|
||||||
|
'--provided-contracts', type=utils.str2dict,
|
||||||
|
help=_('Dictionary of provided contract uuids'))
|
||||||
|
parser.add_argument(
|
||||||
|
'--consumed-contracts', type=utils.str2dict,
|
||||||
|
help=_('Dictionary of consumed contract uuids'))
|
||||||
|
parser.add_argument(
|
||||||
|
'--subnets', type=string.split,
|
||||||
|
help=_('Subnet to map the group'))
|
||||||
|
|
||||||
|
def args2body(self, parsed_args):
|
||||||
|
body = {self.resource: {}, }
|
||||||
|
|
||||||
|
if parsed_args.l2_policy:
|
||||||
|
body[self.resource]['l2_policy_id'] = \
|
||||||
|
neutronV20.find_resourceid_by_name_or_id(
|
||||||
|
self.get_client(), 'l2_policy',
|
||||||
|
parsed_args.l2_policy)
|
||||||
|
|
||||||
|
if parsed_args.network_service_policy:
|
||||||
|
body[self.resource]['network_service_policy_id'] = \
|
||||||
|
neutronV20.find_resourceid_by_name_or_id(
|
||||||
|
self.get_client(), 'network_service_policy',
|
||||||
|
parsed_args.l2_policy)
|
||||||
|
|
||||||
|
if parsed_args.provided_contracts:
|
||||||
|
for key in parsed_args.provided_contracts.keys():
|
||||||
|
id_key = neutronV20.find_resourceid_by_name_or_id(
|
||||||
|
self.get_client(), 'contract',
|
||||||
|
key)
|
||||||
|
parsed_args.provided_contracts[id_key] = \
|
||||||
|
parsed_args.provided_contracts.pop(key)
|
||||||
|
|
||||||
|
if parsed_args.consumed_contracts:
|
||||||
|
for key in parsed_args.consumed_contracts.keys():
|
||||||
|
id_key = neutronV20.find_resourceid_by_name_or_id(
|
||||||
|
self.get_client(), 'contract',
|
||||||
|
key)
|
||||||
|
parsed_args.consumed_contracts[id_key] = \
|
||||||
|
parsed_args.consumed_contracts.pop(key)
|
||||||
|
|
||||||
|
if parsed_args.subnets:
|
||||||
|
for subnet in parsed_args.subnets:
|
||||||
|
subnet_id = neutronV20.find_resourceid_by_name_or_id(
|
||||||
|
self.get_client(), 'subnet',
|
||||||
|
subnet)
|
||||||
|
parsed_args.subnets.remove(subnet)
|
||||||
|
parsed_args.subnets.append(subnet_id)
|
||||||
|
|
||||||
|
neutronV20.update_dict(parsed_args, body[self.resource],
|
||||||
|
['name', 'tenant_id', 'description',
|
||||||
|
'provided_contracts', 'subnets',
|
||||||
|
'consumed_contracts'])
|
||||||
|
|
||||||
|
return body
|
||||||
|
|
||||||
|
|
||||||
|
class ListL2Policy(neutronV20.ListCommand):
|
||||||
|
"""List L2 Policies that belong to a given tenant."""
|
||||||
|
|
||||||
|
resource = 'l2_policy'
|
||||||
|
log = logging.getLogger(__name__ + '.ListL2Policy')
|
||||||
|
_formatters = {}
|
||||||
|
list_columns = ['id', 'name', 'description', 'l3_policy_id']
|
||||||
|
pagination_support = True
|
||||||
|
sorting_support = True
|
||||||
|
|
||||||
|
|
||||||
|
class ShowL2Policy(neutronV20.ShowCommand):
|
||||||
|
"""Show information of a given l2_policy."""
|
||||||
|
|
||||||
|
resource = 'l2_policy'
|
||||||
|
log = logging.getLogger(__name__ + '.ShowL2Policy')
|
||||||
|
|
||||||
|
|
||||||
|
class CreateL2Policy(neutronV20.CreateCommand):
|
||||||
|
"""Create a bridge_domain for a given tenant."""
|
||||||
|
|
||||||
|
resource = 'l2_policy'
|
||||||
|
log = logging.getLogger(__name__ + '.CreateL2Policy')
|
||||||
|
|
||||||
|
def add_known_arguments(self, parser):
|
||||||
|
parser.add_argument(
|
||||||
|
'--description',
|
||||||
|
help=_('Description of the l2_policy'))
|
||||||
|
parser.add_argument(
|
||||||
|
'--network',
|
||||||
|
help=_('Network to map the l2_policy'))
|
||||||
|
parser.add_argument(
|
||||||
|
'--l3-policy',
|
||||||
|
default='',
|
||||||
|
help=_('l3_policy uuid'))
|
||||||
|
parser.add_argument(
|
||||||
|
'name', metavar='NAME',
|
||||||
|
help=_('Name of l2_policy to create'))
|
||||||
|
|
||||||
|
def args2body(self, parsed_args):
|
||||||
|
body = {self.resource: {}, }
|
||||||
|
|
||||||
|
neutronV20.update_dict(parsed_args, body[self.resource],
|
||||||
|
['name', 'tenant_id', 'description'])
|
||||||
|
if parsed_args.l3_policy:
|
||||||
|
body[self.resource]['l3_policy_id'] = \
|
||||||
|
neutronV20.find_resourceid_by_name_or_id(
|
||||||
|
self.get_client(), 'l3_policy',
|
||||||
|
parsed_args.l3_policy)
|
||||||
|
if parsed_args.network:
|
||||||
|
body[self.resource]['network_id'] = \
|
||||||
|
neutronV20.find_resourceid_by_name_or_id(
|
||||||
|
self.get_client(), 'network',
|
||||||
|
parsed_args.network)
|
||||||
|
return body
|
||||||
|
|
||||||
|
|
||||||
|
class DeleteL2Policy(neutronV20.DeleteCommand):
|
||||||
|
"""Delete a given l2_policy."""
|
||||||
|
|
||||||
|
resource = 'l2_policy'
|
||||||
|
log = logging.getLogger(__name__ + '.DeleteL2Policy')
|
||||||
|
|
||||||
|
|
||||||
|
class UpdateL2Policy(neutronV20.UpdateCommand):
|
||||||
|
"""Update l2_policy's information."""
|
||||||
|
|
||||||
|
resource = 'l2_policy'
|
||||||
|
log = logging.getLogger(__name__ + '.UpdateL2Policy')
|
||||||
|
|
||||||
|
|
||||||
|
class ListL3Policy(neutronV20.ListCommand):
|
||||||
|
"""List l3_policies that belong to a given tenant."""
|
||||||
|
|
||||||
|
resource = 'l3_policy'
|
||||||
|
log = logging.getLogger(__name__ + '.ListL3Policy')
|
||||||
|
_formatters = {}
|
||||||
|
list_columns = ['id', 'name', 'description', 'ip_pool',
|
||||||
|
'subnet_prefix_length']
|
||||||
|
pagination_support = True
|
||||||
|
sorting_support = True
|
||||||
|
|
||||||
|
|
||||||
|
class ShowL3Policy(neutronV20.ShowCommand):
|
||||||
|
"""Show information of a given l3_policy."""
|
||||||
|
|
||||||
|
resource = 'l3_policy'
|
||||||
|
log = logging.getLogger(__name__ + '.ShowL3Policy')
|
||||||
|
|
||||||
|
|
||||||
|
class CreateL3Policy(neutronV20.CreateCommand):
|
||||||
|
"""Create a l3_policy for a given tenant."""
|
||||||
|
|
||||||
|
resource = 'l3_policy'
|
||||||
|
log = logging.getLogger(__name__ + '.CreateL3Policy')
|
||||||
|
|
||||||
|
def add_known_arguments(self, parser):
|
||||||
|
parser.add_argument(
|
||||||
|
'--description',
|
||||||
|
help=_('Description of the l3_policy'))
|
||||||
|
parser.add_argument(
|
||||||
|
'--ip-version',
|
||||||
|
type=int,
|
||||||
|
default=4, choices=[4, 6],
|
||||||
|
help=_('IP version, default is 4'))
|
||||||
|
parser.add_argument(
|
||||||
|
'--ip-pool',
|
||||||
|
help=_('CIDR of IP pool to create, default is 10.0.0.0/8'))
|
||||||
|
parser.add_argument(
|
||||||
|
'--subnet-prefix-length',
|
||||||
|
type=int,
|
||||||
|
default=24,
|
||||||
|
help=_('Subnet prefix length, default is 24'))
|
||||||
|
parser.add_argument(
|
||||||
|
'name', metavar='NAME',
|
||||||
|
help=_('Name of l3_policy to create'))
|
||||||
|
|
||||||
|
def args2body(self, parsed_args):
|
||||||
|
body = {self.resource: {}, }
|
||||||
|
|
||||||
|
neutronV20.update_dict(parsed_args, body[self.resource],
|
||||||
|
['name', 'tenant_id', 'description',
|
||||||
|
'ip_version', 'ip_pool',
|
||||||
|
'subnet_prefix_length'])
|
||||||
|
|
||||||
|
return body
|
||||||
|
|
||||||
|
|
||||||
|
class DeleteL3Policy(neutronV20.DeleteCommand):
|
||||||
|
"""Delete a given l3_policy."""
|
||||||
|
|
||||||
|
resource = 'l3_policy'
|
||||||
|
log = logging.getLogger(__name__ + '.DeleteL3Policy')
|
||||||
|
|
||||||
|
|
||||||
|
class UpdateL3Policy(neutronV20.UpdateCommand):
|
||||||
|
"""Update l3_policy's information."""
|
||||||
|
|
||||||
|
resource = 'l3_policy'
|
||||||
|
log = logging.getLogger(__name__ + '.UpdateL3Policy')
|
||||||
|
|
||||||
|
|
||||||
|
class ListNetworkServicePolicy(neutronV20.ListCommand):
|
||||||
|
"""List Network Service Policies that belong to a given tenant."""
|
||||||
|
|
||||||
|
resource = 'network_service_policy'
|
||||||
|
log = logging.getLogger(__name__ + '.ListNetworkServicePolicy')
|
||||||
|
_formatters = {'network_servie_params': _format_network_service_params}
|
||||||
|
list_columns = ['id', 'name', 'description', 'network_service_params']
|
||||||
|
pagination_support = True
|
||||||
|
sorting_support = True
|
||||||
|
|
||||||
|
|
||||||
|
class ShowNetworkServicePolicy(neutronV20.ShowCommand):
|
||||||
|
"""Show information of a given network_service_policy."""
|
||||||
|
|
||||||
|
resource = 'network_service_policy'
|
||||||
|
log = logging.getLogger(__name__ + '.ShowNetworkServicePolicy')
|
||||||
|
|
||||||
|
|
||||||
|
class CreateNetworkServicePolicy(neutronV20.CreateCommand):
|
||||||
|
"""Create a Network Service Policy for a given tenant."""
|
||||||
|
|
||||||
|
resource = 'network_service_policy'
|
||||||
|
log = logging.getLogger(__name__ + '.CreateNetworkServicePolicy')
|
||||||
|
|
||||||
|
def add_known_arguments(self, parser):
|
||||||
|
parser.add_argument(
|
||||||
|
'--description',
|
||||||
|
help=_('Description of the network_service_policy'))
|
||||||
|
parser.add_argument(
|
||||||
|
'name',
|
||||||
|
help=_('Name of network_service_policy to create'))
|
||||||
|
parser.add_argument(
|
||||||
|
'--network-service-params',
|
||||||
|
metavar='type=PARAM_TYPE,name=PARAM_NAME,value=PARAM_VALUE',
|
||||||
|
action='append', dest='network_service_params',
|
||||||
|
type=utils.str2dict,
|
||||||
|
help=_('Network service params for this network service policy'
|
||||||
|
'(This option can be repeated).'))
|
||||||
|
|
||||||
|
def args2body(self, parsed_args):
|
||||||
|
body = {self.resource: {}, }
|
||||||
|
|
||||||
|
"""
|
||||||
|
if parsed_args.name:
|
||||||
|
body[self.resource].update({'name': parsed_args.name})
|
||||||
|
|
||||||
|
if parsed_args.description:
|
||||||
|
body[self.resource].update({'description': parsed_args.name})
|
||||||
|
|
||||||
|
if parsed_args.network_service_params:
|
||||||
|
body[self.resource]['network_service_params'] = (
|
||||||
|
parsed_args.network_sercice_params)
|
||||||
|
"""
|
||||||
|
|
||||||
|
neutronV20.update_dict(parsed_args, body[self.resource],
|
||||||
|
['name', 'tenant_id', 'description',
|
||||||
|
'network_service_params'])
|
||||||
|
return body
|
||||||
|
|
||||||
|
|
||||||
|
class DeleteNetworkServicePolicy(neutronV20.DeleteCommand):
|
||||||
|
"""Delete a given network_service_policy."""
|
||||||
|
|
||||||
|
resource = 'network_service_policy'
|
||||||
|
log = logging.getLogger(__name__ + '.DeleteNetworkServicePolicy')
|
||||||
|
|
||||||
|
|
||||||
|
class UpdateNetworkServicePolicy(neutronV20.UpdateCommand):
|
||||||
|
"""Update network_service_policy's information."""
|
||||||
|
|
||||||
|
resource = 'network_service_policy'
|
||||||
|
log = logging.getLogger(__name__ + '.UpdateNetworkServicePolicy')
|
||||||
|
|
||||||
|
def add_known_arguments(self, parser):
|
||||||
|
parser.add_argument(
|
||||||
|
'--description',
|
||||||
|
help=_('Description of the network_service_policy'))
|
||||||
|
parser.add_argument(
|
||||||
|
'--name',
|
||||||
|
help=_('Name of network_service_policy to create'))
|
||||||
|
parser.add_argument(
|
||||||
|
'--network-service-params',
|
||||||
|
metavar='type=PARAM_TYPE,name=PARAM_NAME,value=PARAM_VALUE',
|
||||||
|
action='append', dest='network_service_params',
|
||||||
|
type=utils.str2dict,
|
||||||
|
help=_('Network service params for this network service policy'
|
||||||
|
'(This option can be repeated).'))
|
||||||
|
|
||||||
|
def args2body(self, parsed_args):
|
||||||
|
body = {self.resource: {}, }
|
||||||
|
|
||||||
|
neutronV20.update_dict(parsed_args, body[self.resource],
|
||||||
|
['name', 'tenant_id', 'description',
|
||||||
|
'network_service_params'])
|
||||||
|
return body
|
||||||
|
|
||||||
|
|
||||||
|
class ListPolicyClassifier(neutronV20.ListCommand):
|
||||||
|
"""List classifiers that belong to a given tenant."""
|
||||||
|
|
||||||
|
resource = 'policy_classifier'
|
||||||
|
log = logging.getLogger(__name__ + '.ListPolicyClassifier')
|
||||||
|
_formatters = {}
|
||||||
|
list_columns = ['id', 'name', 'protocol', 'port_range', 'direction']
|
||||||
|
pagination_support = True
|
||||||
|
sorting_support = True
|
||||||
|
|
||||||
|
|
||||||
|
class ShowPolicyClassifier(neutronV20.ShowCommand):
|
||||||
|
"""Show information of a given classifier."""
|
||||||
|
|
||||||
|
resource = 'policy_classifier'
|
||||||
|
log = logging.getLogger(__name__ + '.ShowPolicyClassifier')
|
||||||
|
|
||||||
|
|
||||||
|
class CreatePolicyClassifier(neutronV20.CreateCommand):
|
||||||
|
"""Create a classifier for a given tenant."""
|
||||||
|
|
||||||
|
resource = 'policy_classifier'
|
||||||
|
log = logging.getLogger(__name__ + '.CreatePolicyClassifier')
|
||||||
|
|
||||||
|
def add_known_arguments(self, parser):
|
||||||
|
parser.add_argument(
|
||||||
|
'--description',
|
||||||
|
help=_('Description of the policy classifier'))
|
||||||
|
parser.add_argument(
|
||||||
|
'--protocol',
|
||||||
|
choices=['tcp', 'udp', 'icmp'],
|
||||||
|
help=_('Protocol'))
|
||||||
|
parser.add_argument(
|
||||||
|
'--port-range',
|
||||||
|
help=_('Port range'))
|
||||||
|
parser.add_argument(
|
||||||
|
'--direction',
|
||||||
|
choices=['in', 'out', 'bi', ''],
|
||||||
|
help=_('Direction'))
|
||||||
|
parser.add_argument(
|
||||||
|
'name', metavar='NAME',
|
||||||
|
help=_('Name of classifier to create'))
|
||||||
|
|
||||||
|
def args2body(self, parsed_args):
|
||||||
|
body = {self.resource: {}, }
|
||||||
|
|
||||||
|
neutronV20.update_dict(parsed_args, body[self.resource],
|
||||||
|
['name', 'tenant_id', 'description',
|
||||||
|
'protocol', 'port_range', 'direction'])
|
||||||
|
|
||||||
|
return body
|
||||||
|
|
||||||
|
|
||||||
|
class DeletePolicyClassifier(neutronV20.DeleteCommand):
|
||||||
|
"""Delete a given classifier."""
|
||||||
|
|
||||||
|
resource = 'policy_classifier'
|
||||||
|
log = logging.getLogger(__name__ + '.DeletePolicyClassifier')
|
||||||
|
|
||||||
|
|
||||||
|
class UpdatePolicyClassifier(neutronV20.UpdateCommand):
|
||||||
|
"""Update classifier's information."""
|
||||||
|
|
||||||
|
resource = 'policy_classifier'
|
||||||
|
log = logging.getLogger(__name__ + '.UpdatePolicyClassifier')
|
||||||
|
|
||||||
|
|
||||||
|
class ListPolicyAction(neutronV20.ListCommand):
|
||||||
|
"""List actions that belong to a given tenant."""
|
||||||
|
|
||||||
|
resource = 'policy_action'
|
||||||
|
log = logging.getLogger(__name__ + '.ListPolicyAction')
|
||||||
|
_formatters = {}
|
||||||
|
list_columns = ['id', 'name', 'action_type', 'action_value']
|
||||||
|
pagination_support = True
|
||||||
|
sorting_support = True
|
||||||
|
|
||||||
|
|
||||||
|
class ShowPolicyAction(neutronV20.ShowCommand):
|
||||||
|
"""Show information of a given action."""
|
||||||
|
|
||||||
|
resource = 'policy_action'
|
||||||
|
log = logging.getLogger(__name__ + '.ShowPolicyAction')
|
||||||
|
|
||||||
|
|
||||||
|
class CreatePolicyAction(neutronV20.CreateCommand):
|
||||||
|
"""Create a action for a given tenant."""
|
||||||
|
|
||||||
|
resource = 'policy_action'
|
||||||
|
log = logging.getLogger(__name__ + '.CreatePolicyAction')
|
||||||
|
|
||||||
|
def add_known_arguments(self, parser):
|
||||||
|
parser.add_argument(
|
||||||
|
'--description',
|
||||||
|
help=_('Description of the policy action'))
|
||||||
|
parser.add_argument(
|
||||||
|
'--action-type',
|
||||||
|
help=_('Type of action'))
|
||||||
|
parser.add_argument(
|
||||||
|
'--action-value',
|
||||||
|
help=_('uuid of service for redirect action'))
|
||||||
|
parser.add_argument(
|
||||||
|
'name', metavar='NAME',
|
||||||
|
help=_('Name of action to create'))
|
||||||
|
|
||||||
|
def args2body(self, parsed_args):
|
||||||
|
body = {self.resource: {}, }
|
||||||
|
|
||||||
|
neutronV20.update_dict(parsed_args, body[self.resource],
|
||||||
|
['name', 'tenant_id', 'description',
|
||||||
|
'action_type', 'action_value'])
|
||||||
|
|
||||||
|
return body
|
||||||
|
|
||||||
|
|
||||||
|
class DeletePolicyAction(neutronV20.DeleteCommand):
|
||||||
|
"""Delete a given action."""
|
||||||
|
|
||||||
|
resource = 'policy_action'
|
||||||
|
log = logging.getLogger(__name__ + '.DeletePolicyAction')
|
||||||
|
|
||||||
|
|
||||||
|
class UpdatePolicyAction(neutronV20.UpdateCommand):
|
||||||
|
"""Update action's information."""
|
||||||
|
|
||||||
|
resource = 'policy_action'
|
||||||
|
log = logging.getLogger(__name__ + '.UpdatePolicyAction')
|
||||||
|
|
||||||
|
|
||||||
|
class ListPolicyRule(neutronV20.ListCommand):
|
||||||
|
"""List policy_rules that belong to a given tenant."""
|
||||||
|
|
||||||
|
resource = 'policy_rule'
|
||||||
|
log = logging.getLogger(__name__ + '.ListPolicyRule')
|
||||||
|
_formatters = {}
|
||||||
|
list_columns = ['id', 'name', 'enabled', 'classifier_id',
|
||||||
|
'actions']
|
||||||
|
pagination_support = True
|
||||||
|
sorting_support = True
|
||||||
|
|
||||||
|
|
||||||
|
class ShowPolicyRule(neutronV20.ShowCommand):
|
||||||
|
"""Show information of a given policy_rule."""
|
||||||
|
|
||||||
|
resource = 'policy_rule'
|
||||||
|
log = logging.getLogger(__name__ + '.ShowPolicyRule')
|
||||||
|
|
||||||
|
|
||||||
|
class CreatePolicyRule(neutronV20.CreateCommand):
|
||||||
|
"""Create a policy_rule for a given tenant."""
|
||||||
|
|
||||||
|
resource = 'policy_rule'
|
||||||
|
log = logging.getLogger(__name__ + '.CreatePolicyRule')
|
||||||
|
|
||||||
|
def add_known_arguments(self, parser):
|
||||||
|
parser.add_argument(
|
||||||
|
'--description',
|
||||||
|
help=_('Description of the policy_rule'))
|
||||||
|
parser.add_argument(
|
||||||
|
'--enabled', type=bool,
|
||||||
|
help=_('Enable flag'))
|
||||||
|
parser.add_argument(
|
||||||
|
'--classifier',
|
||||||
|
help=_('uuid of policy classifier'))
|
||||||
|
parser.add_argument(
|
||||||
|
'--actions', type=string.split,
|
||||||
|
help=_('List of policy actions'))
|
||||||
|
parser.add_argument(
|
||||||
|
'name', metavar='NAME',
|
||||||
|
help=_('Name of policy_rule to create'))
|
||||||
|
|
||||||
|
def args2body(self, parsed_args):
|
||||||
|
body = {self.resource: {}, }
|
||||||
|
|
||||||
|
if parsed_args.actions:
|
||||||
|
body[self.resource]['policy_actions'] = [
|
||||||
|
neutronV20.find_resourceid_by_name_or_id(
|
||||||
|
self.get_client(),
|
||||||
|
'policy_action',
|
||||||
|
elem) for elem in parsed_args.actions]
|
||||||
|
|
||||||
|
if parsed_args.classifier:
|
||||||
|
body[self.resource]['policy_classifier_id'] = \
|
||||||
|
neutronV20.find_resourceid_by_name_or_id(
|
||||||
|
self.get_client(),
|
||||||
|
'policy_classifier',
|
||||||
|
parsed_args.classifier)
|
||||||
|
|
||||||
|
neutronV20.update_dict(parsed_args, body[self.resource],
|
||||||
|
['name', 'tenant_id', 'description',
|
||||||
|
'enabled'])
|
||||||
|
|
||||||
|
return body
|
||||||
|
|
||||||
|
|
||||||
|
class DeletePolicyRule(neutronV20.DeleteCommand):
|
||||||
|
"""Delete a given policy_rule."""
|
||||||
|
|
||||||
|
resource = 'policy_rule'
|
||||||
|
log = logging.getLogger(__name__ + '.DeletePolicyRule')
|
||||||
|
|
||||||
|
|
||||||
|
class UpdatePolicyRule(neutronV20.UpdateCommand):
|
||||||
|
"""Update policy_rule's information."""
|
||||||
|
|
||||||
|
resource = 'policy_rule'
|
||||||
|
log = logging.getLogger(__name__ + '.UpdatePolicyRule')
|
||||||
|
|
||||||
|
def add_known_arguments(self, parser):
|
||||||
|
parser.add_argument(
|
||||||
|
'--enabled', type=bool,
|
||||||
|
help=_('Enable flag'))
|
||||||
|
parser.add_argument(
|
||||||
|
'--classifier',
|
||||||
|
help=_('uuid of policy classifier'))
|
||||||
|
parser.add_argument(
|
||||||
|
'--actions', type=string.split,
|
||||||
|
help=_('List of policy actions'))
|
||||||
|
|
||||||
|
def args2body(self, parsed_args):
|
||||||
|
body = {self.resource: {}, }
|
||||||
|
|
||||||
|
if parsed_args.actions:
|
||||||
|
body[self.resource]['policy_actions'] = [
|
||||||
|
neutronV20.find_resourceid_by_name_or_id(
|
||||||
|
self.get_client(),
|
||||||
|
'policy_action',
|
||||||
|
elem) for elem in parsed_args.actions]
|
||||||
|
|
||||||
|
if parsed_args.classifier:
|
||||||
|
body[self.resource]['policy_classifier_id'] = \
|
||||||
|
neutronV20.find_resourceid_by_name_or_id(
|
||||||
|
self.get_client(),
|
||||||
|
'policy_classifier',
|
||||||
|
parsed_args.classifier)
|
||||||
|
|
||||||
|
neutronV20.update_dict(parsed_args, body[self.resource],
|
||||||
|
['name', 'description',
|
||||||
|
'enabled'])
|
||||||
|
return body
|
||||||
|
|
||||||
|
|
||||||
|
class ListContract(neutronV20.ListCommand):
|
||||||
|
"""List contracts that belong to a given tenant."""
|
||||||
|
|
||||||
|
resource = 'contract'
|
||||||
|
log = logging.getLogger(__name__ + '.ListContract')
|
||||||
|
_formatters = {}
|
||||||
|
list_columns = ['id', 'name', 'ploicy_rules']
|
||||||
|
pagination_support = True
|
||||||
|
sorting_support = True
|
||||||
|
|
||||||
|
|
||||||
|
class ShowContract(neutronV20.ShowCommand):
|
||||||
|
"""Show information of a given contract."""
|
||||||
|
|
||||||
|
resource = 'contract'
|
||||||
|
log = logging.getLogger(__name__ + '.ShowContract')
|
||||||
|
|
||||||
|
|
||||||
|
class CreateContract(neutronV20.CreateCommand):
|
||||||
|
"""Create a contract for a given tenant."""
|
||||||
|
|
||||||
|
resource = 'contract'
|
||||||
|
log = logging.getLogger(__name__ + '.CreateContract')
|
||||||
|
|
||||||
|
def add_known_arguments(self, parser):
|
||||||
|
parser.add_argument(
|
||||||
|
'--description',
|
||||||
|
help=_('Description of the contract'))
|
||||||
|
parser.add_argument(
|
||||||
|
'--policy-rules', type=string.split,
|
||||||
|
help=_('List of policy rules'))
|
||||||
|
parser.add_argument(
|
||||||
|
'--child-contracts', type=string.split,
|
||||||
|
help=_('List of child contracts'))
|
||||||
|
parser.add_argument(
|
||||||
|
'name', metavar='NAME',
|
||||||
|
help=_('Name of contract to create'))
|
||||||
|
|
||||||
|
def args2body(self, parsed_args):
|
||||||
|
body = {self.resource: {}, }
|
||||||
|
|
||||||
|
if parsed_args.policy_rules:
|
||||||
|
body[self.resource]['policy_rules'] = [
|
||||||
|
neutronV20.find_resourceid_by_name_or_id(
|
||||||
|
self.get_client(),
|
||||||
|
'policy_rule',
|
||||||
|
elem) for elem in parsed_args.policy_rules]
|
||||||
|
|
||||||
|
if parsed_args.child_contracts:
|
||||||
|
body[self.resource]['child_contracts'] = [
|
||||||
|
neutronV20.find_resourceid_by_name_or_id(
|
||||||
|
self.get_client(),
|
||||||
|
'contract',
|
||||||
|
elem) for elem in parsed_args.child_contracts]
|
||||||
|
|
||||||
|
neutronV20.update_dict(parsed_args, body[self.resource],
|
||||||
|
['name', 'tenant_id', 'description'])
|
||||||
|
return body
|
||||||
|
|
||||||
|
|
||||||
|
class DeleteContract(neutronV20.DeleteCommand):
|
||||||
|
"""Delete a given contract."""
|
||||||
|
|
||||||
|
resource = 'contract'
|
||||||
|
log = logging.getLogger(__name__ + '.DeleteContract')
|
||||||
|
|
||||||
|
|
||||||
|
class UpdateContract(neutronV20.UpdateCommand):
|
||||||
|
"""Update contract's information."""
|
||||||
|
|
||||||
|
resource = 'contract'
|
||||||
|
log = logging.getLogger(__name__ + '.UpdateContract')
|
||||||
|
|
||||||
|
def add_known_arguments(self, parser):
|
||||||
|
parser.add_argument(
|
||||||
|
'--policy-rules', type=string.split,
|
||||||
|
help=_('List of policy rules'))
|
||||||
|
parser.add_argument(
|
||||||
|
'--child-contracts', type=string.split,
|
||||||
|
help=_('List of child contracts'))
|
||||||
|
|
||||||
|
def args2body(self, parsed_args):
|
||||||
|
body = {self.resource: {}, }
|
||||||
|
if parsed_args.policy_rules:
|
||||||
|
body[self.resource]['policy_rules'] = [
|
||||||
|
neutronV20.find_resourceid_by_name_or_id(
|
||||||
|
self.get_client(),
|
||||||
|
'policy_rule',
|
||||||
|
elem) for elem in parsed_args.policy_rules]
|
||||||
|
parsed_args.policy_rules = body[self.resource]['policy_rules']
|
||||||
|
|
||||||
|
if parsed_args.child_contracts:
|
||||||
|
body[self.resource]['child_contracts'] = [
|
||||||
|
neutronV20.find_resourceid_by_name_or_id(
|
||||||
|
self.get_client(),
|
||||||
|
'contract',
|
||||||
|
elem) for elem in parsed_args.child_contracts]
|
||||||
|
parsed_args.child_contracts = parsed_args.child_contracts
|
||||||
|
neutronV20.update_dict(parsed_args, body[self.resource],
|
||||||
|
['name', 'description', 'policy_rules',
|
||||||
|
'child_contracts'])
|
||||||
|
return body
|
793
gbpclient/gbpshell.py
Normal file
793
gbpclient/gbpshell.py
Normal file
@ -0,0 +1,793 @@
|
|||||||
|
# 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.
|
||||||
|
#
|
||||||
|
|
||||||
|
"""
|
||||||
|
Command-line interface to the GBP APIs
|
||||||
|
"""
|
||||||
|
|
||||||
|
from __future__ import print_function
|
||||||
|
|
||||||
|
import argparse
|
||||||
|
import logging
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
|
||||||
|
from keystoneclient.auth.identity import v2 as v2_auth
|
||||||
|
from keystoneclient.auth.identity import v3 as v3_auth
|
||||||
|
from keystoneclient import discover
|
||||||
|
from keystoneclient.openstack.common.apiclient import exceptions as ks_exc
|
||||||
|
from keystoneclient import session
|
||||||
|
import six.moves.urllib.parse as urlparse
|
||||||
|
|
||||||
|
from cliff import app
|
||||||
|
from cliff import commandmanager
|
||||||
|
from neutronclient.common import clientmanager
|
||||||
|
from neutronclient.common import exceptions as exc
|
||||||
|
from neutronclient.common import utils
|
||||||
|
from neutronclient.openstack.common.gettextutils import _
|
||||||
|
from neutronclient.openstack.common import strutils
|
||||||
|
from neutronclient.version import __version__
|
||||||
|
|
||||||
|
from gbpclient.gbp.v2_0 import groupbasedpolicy as gbp
|
||||||
|
|
||||||
|
VERSION = '2.0'
|
||||||
|
NEUTRON_API_VERSION = '2.0'
|
||||||
|
clientmanager.neutron_client.API_VERSIONS = {
|
||||||
|
'2.0': 'gbpclient.v2_0.client.Client',
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def run_command(cmd, cmd_parser, sub_argv):
|
||||||
|
_argv = sub_argv
|
||||||
|
index = -1
|
||||||
|
values_specs = []
|
||||||
|
if '--' in sub_argv:
|
||||||
|
index = sub_argv.index('--')
|
||||||
|
_argv = sub_argv[:index]
|
||||||
|
values_specs = sub_argv[index:]
|
||||||
|
known_args, _values_specs = cmd_parser.parse_known_args(_argv)
|
||||||
|
cmd.values_specs = (index == -1 and _values_specs or values_specs)
|
||||||
|
return cmd.run(known_args)
|
||||||
|
|
||||||
|
|
||||||
|
def env(*_vars, **kwargs):
|
||||||
|
"""Search for the first defined of possibly many env vars.
|
||||||
|
|
||||||
|
Returns the first environment variable defined in vars, or
|
||||||
|
returns the default defined in kwargs.
|
||||||
|
|
||||||
|
"""
|
||||||
|
for v in _vars:
|
||||||
|
value = os.environ.get(v, None)
|
||||||
|
if value:
|
||||||
|
return value
|
||||||
|
return kwargs.get('default', '')
|
||||||
|
|
||||||
|
|
||||||
|
def check_non_negative_int(value):
|
||||||
|
try:
|
||||||
|
value = int(value)
|
||||||
|
except ValueError:
|
||||||
|
raise argparse.ArgumentTypeError(_("invalid int value: %r") % value)
|
||||||
|
if value < 0:
|
||||||
|
raise argparse.ArgumentTypeError(_("input value %d is negative") %
|
||||||
|
value)
|
||||||
|
return value
|
||||||
|
|
||||||
|
|
||||||
|
COMMAND_V2 = {
|
||||||
|
'policy-target-create': gbp.CreateEndpoint,
|
||||||
|
'policy-target-delete': gbp.DeleteEndpoint,
|
||||||
|
'policy-target-update': gbp.UpdateEndpoint,
|
||||||
|
'policy-target-list': gbp.ListEndpoint,
|
||||||
|
'policy-target-show': gbp.ShowEndpoint,
|
||||||
|
'group-create': gbp.CreateEndpointGroup,
|
||||||
|
'group-delete': gbp.DeleteEndpointGroup,
|
||||||
|
'group-update': gbp.UpdateEndpointGroup,
|
||||||
|
'group-list': gbp.ListEndpointGroup,
|
||||||
|
'group-show': gbp.ShowEndpointGroup,
|
||||||
|
'l2policy-create': gbp.CreateL2Policy,
|
||||||
|
'l2policy-delete': gbp.DeleteL2Policy,
|
||||||
|
'l2policy-update': gbp.UpdateL2Policy,
|
||||||
|
'l2policy-list': gbp.ListL2Policy,
|
||||||
|
'l2policy-show': gbp.ShowL2Policy,
|
||||||
|
'l3policy-create': gbp.CreateL3Policy,
|
||||||
|
'l3policy-delete': gbp.DeleteL3Policy,
|
||||||
|
'l3policy-update': gbp.UpdateL3Policy,
|
||||||
|
'l3policy-list': gbp.ListL3Policy,
|
||||||
|
'l3policy-show': gbp.ShowL3Policy,
|
||||||
|
'network-service-policy-create': gbp.CreateNetworkServicePolicy,
|
||||||
|
'network-service-policy-delete': gbp.DeleteNetworkServicePolicy,
|
||||||
|
'network-service-policy-update': gbp.UpdateNetworkServicePolicy,
|
||||||
|
'network-service-policy-list': gbp.ListNetworkServicePolicy,
|
||||||
|
'network-service-policy-show': gbp.ShowNetworkServicePolicy,
|
||||||
|
'policy-classifier-create': gbp.CreatePolicyClassifier,
|
||||||
|
'policy-classifier-delete': gbp.DeletePolicyClassifier,
|
||||||
|
'policy-classifier-update': gbp.UpdatePolicyClassifier,
|
||||||
|
'policy-classifier-list': gbp.ListPolicyClassifier,
|
||||||
|
'policy-classifier-show': gbp.ShowPolicyClassifier,
|
||||||
|
'policy-action-create': gbp.CreatePolicyAction,
|
||||||
|
'policy-action-delete': gbp.DeletePolicyAction,
|
||||||
|
'policy-action-update': gbp.UpdatePolicyAction,
|
||||||
|
'policy-action-list': gbp.ListPolicyAction,
|
||||||
|
'policy-action-show': gbp.ShowPolicyAction,
|
||||||
|
'policy-rule-create': gbp.CreatePolicyRule,
|
||||||
|
'policy-rule-delete': gbp.DeletePolicyRule,
|
||||||
|
'policy-rule-update': gbp.UpdatePolicyRule,
|
||||||
|
'policy-rule-list': gbp.ListPolicyRule,
|
||||||
|
'policy-rule-show': gbp.ShowPolicyRule,
|
||||||
|
'policy-rule-set-create': gbp.CreateContract,
|
||||||
|
'policy-rule-set-delete': gbp.DeleteContract,
|
||||||
|
'policy-rule-set-update': gbp.UpdateContract,
|
||||||
|
'policy-rule-set-list': gbp.ListContract,
|
||||||
|
'policy-rule-set-show': gbp.ShowContract,
|
||||||
|
}
|
||||||
|
|
||||||
|
COMMANDS = {'2.0': COMMAND_V2}
|
||||||
|
|
||||||
|
|
||||||
|
class HelpAction(argparse.Action):
|
||||||
|
"""Provide a custom action so the -h and --help options
|
||||||
|
to the main app will print a list of the commands.
|
||||||
|
|
||||||
|
The commands are determined by checking the CommandManager
|
||||||
|
instance, passed in as the "default" value for the action.
|
||||||
|
"""
|
||||||
|
def __call__(self, parser, namespace, values, option_string=None):
|
||||||
|
outputs = []
|
||||||
|
max_len = 0
|
||||||
|
app = self.default
|
||||||
|
parser.print_help(app.stdout)
|
||||||
|
app.api_version = '2.0' # Check this
|
||||||
|
app.stdout.write(_('\nCommands for GBP API v%s:\n') % app.api_version)
|
||||||
|
command_manager = app.command_manager
|
||||||
|
for name, ep in sorted(command_manager):
|
||||||
|
factory = ep.load()
|
||||||
|
cmd = factory(self, None)
|
||||||
|
one_liner = cmd.get_description().split('\n')[0]
|
||||||
|
outputs.append((name, one_liner))
|
||||||
|
max_len = max(len(name), max_len)
|
||||||
|
for (name, one_liner) in outputs:
|
||||||
|
app.stdout.write(' %s %s\n' % (name.ljust(max_len), one_liner))
|
||||||
|
sys.exit(0)
|
||||||
|
|
||||||
|
|
||||||
|
class GBPShell(app.App):
|
||||||
|
|
||||||
|
# verbose logging levels
|
||||||
|
WARNING_LEVEL = 0
|
||||||
|
INFO_LEVEL = 1
|
||||||
|
DEBUG_LEVEL = 2
|
||||||
|
CONSOLE_MESSAGE_FORMAT = '%(message)s'
|
||||||
|
DEBUG_MESSAGE_FORMAT = '%(levelname)s: %(name)s %(message)s'
|
||||||
|
log = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
def __init__(self, apiversion):
|
||||||
|
super(GBPShell, self).__init__(
|
||||||
|
description=__doc__.strip(),
|
||||||
|
version=VERSION,
|
||||||
|
command_manager=commandmanager.CommandManager('gbp.cli'), )
|
||||||
|
self.commands = COMMANDS
|
||||||
|
for k, v in self.commands[apiversion].items():
|
||||||
|
self.command_manager.add_command(k, v)
|
||||||
|
|
||||||
|
# This is instantiated in initialize_app() only when using
|
||||||
|
# password flow auth
|
||||||
|
self.auth_client = None
|
||||||
|
self.api_version = apiversion
|
||||||
|
|
||||||
|
def build_option_parser(self, description, version):
|
||||||
|
"""Return an argparse option parser for this application.
|
||||||
|
|
||||||
|
Subclasses may override this method to extend
|
||||||
|
the parser with more global options.
|
||||||
|
|
||||||
|
:param description: full description of the application
|
||||||
|
:paramtype description: str
|
||||||
|
:param version: version number for the application
|
||||||
|
:paramtype version: str
|
||||||
|
"""
|
||||||
|
parser = argparse.ArgumentParser(
|
||||||
|
description=description,
|
||||||
|
add_help=False, )
|
||||||
|
parser.add_argument(
|
||||||
|
'--version',
|
||||||
|
action='version',
|
||||||
|
version=__version__, )
|
||||||
|
parser.add_argument(
|
||||||
|
'-v', '--verbose', '--debug',
|
||||||
|
action='count',
|
||||||
|
dest='verbose_level',
|
||||||
|
default=self.DEFAULT_VERBOSE_LEVEL,
|
||||||
|
help=_('Increase verbosity of output and show tracebacks on'
|
||||||
|
' errors. You can repeat this option.'))
|
||||||
|
parser.add_argument(
|
||||||
|
'-q', '--quiet',
|
||||||
|
action='store_const',
|
||||||
|
dest='verbose_level',
|
||||||
|
const=0,
|
||||||
|
help=_('Suppress output except warnings and errors.'))
|
||||||
|
parser.add_argument(
|
||||||
|
'-h', '--help',
|
||||||
|
action=HelpAction,
|
||||||
|
nargs=0,
|
||||||
|
default=self, # tricky
|
||||||
|
help=_("Show this help message and exit."))
|
||||||
|
parser.add_argument(
|
||||||
|
'-r', '--retries',
|
||||||
|
metavar="NUM",
|
||||||
|
type=check_non_negative_int,
|
||||||
|
default=0,
|
||||||
|
help=_("How many times the request to the Neutron server should "
|
||||||
|
"be retried if it fails."))
|
||||||
|
# FIXME(bklei): this method should come from python-keystoneclient
|
||||||
|
self._append_global_identity_args(parser)
|
||||||
|
|
||||||
|
return parser
|
||||||
|
|
||||||
|
def _append_global_identity_args(self, parser):
|
||||||
|
# FIXME(bklei): these are global identity (Keystone) arguments which
|
||||||
|
# should be consistent and shared by all service clients. Therefore,
|
||||||
|
# they should be provided by python-keystoneclient. We will need to
|
||||||
|
# refactor this code once this functionality is available in
|
||||||
|
# python-keystoneclient.
|
||||||
|
#
|
||||||
|
# Note: At that time we'll need to decide if we can just abandon
|
||||||
|
# the deprecated args (--service-type and --endpoint-type).
|
||||||
|
|
||||||
|
parser.add_argument(
|
||||||
|
'--os-service-type', metavar='<os-service-type>',
|
||||||
|
default=env('OS_NETWORK_SERVICE_TYPE', default='network'),
|
||||||
|
help=_('Defaults to env[OS_NETWORK_SERVICE_TYPE] or network.'))
|
||||||
|
|
||||||
|
parser.add_argument(
|
||||||
|
'--os-endpoint-type', metavar='<os-endpoint-type>',
|
||||||
|
default=env('OS_ENDPOINT_TYPE', default='publicURL'),
|
||||||
|
help=_('Defaults to env[OS_ENDPOINT_TYPE] or publicURL.'))
|
||||||
|
|
||||||
|
# FIXME(bklei): --service-type is deprecated but kept in for
|
||||||
|
# backward compatibility.
|
||||||
|
parser.add_argument(
|
||||||
|
'--service-type', metavar='<service-type>',
|
||||||
|
default=env('OS_NETWORK_SERVICE_TYPE', default='network'),
|
||||||
|
help=_('DEPRECATED! Use --os-service-type.'))
|
||||||
|
|
||||||
|
# FIXME(bklei): --endpoint-type is deprecated but kept in for
|
||||||
|
# backward compatibility.
|
||||||
|
parser.add_argument(
|
||||||
|
'--endpoint-type', metavar='<endpoint-type>',
|
||||||
|
default=env('OS_ENDPOINT_TYPE', default='publicURL'),
|
||||||
|
help=_('DEPRECATED! Use --os-endpoint-type.'))
|
||||||
|
|
||||||
|
parser.add_argument(
|
||||||
|
'--os-auth-strategy', metavar='<auth-strategy>',
|
||||||
|
default=env('OS_AUTH_STRATEGY', default='keystone'),
|
||||||
|
help=_('DEPRECATED! Only keystone is supported.'))
|
||||||
|
|
||||||
|
parser.add_argument(
|
||||||
|
'--os_auth_strategy',
|
||||||
|
help=argparse.SUPPRESS)
|
||||||
|
|
||||||
|
parser.add_argument(
|
||||||
|
'--os-auth-url', metavar='<auth-url>',
|
||||||
|
default=env('OS_AUTH_URL'),
|
||||||
|
help=_('Authentication URL, defaults to env[OS_AUTH_URL].'))
|
||||||
|
parser.add_argument(
|
||||||
|
'--os_auth_url',
|
||||||
|
help=argparse.SUPPRESS)
|
||||||
|
|
||||||
|
project_name_group = parser.add_mutually_exclusive_group()
|
||||||
|
project_name_group.add_argument(
|
||||||
|
'--os-tenant-name', metavar='<auth-tenant-name>',
|
||||||
|
default=env('OS_TENANT_NAME'),
|
||||||
|
help=_('Authentication tenant name, defaults to '
|
||||||
|
'env[OS_TENANT_NAME].'))
|
||||||
|
project_name_group.add_argument(
|
||||||
|
'--os-project-name',
|
||||||
|
metavar='<auth-project-name>',
|
||||||
|
default=utils.env('OS_PROJECT_NAME'),
|
||||||
|
help='Another way to specify tenant name. '
|
||||||
|
'This option is mutually exclusive with '
|
||||||
|
' --os-tenant-name. '
|
||||||
|
'Defaults to env[OS_PROJECT_NAME].')
|
||||||
|
|
||||||
|
parser.add_argument(
|
||||||
|
'--os_tenant_name',
|
||||||
|
help=argparse.SUPPRESS)
|
||||||
|
|
||||||
|
project_id_group = parser.add_mutually_exclusive_group()
|
||||||
|
project_id_group.add_argument(
|
||||||
|
'--os-tenant-id', metavar='<auth-tenant-id>',
|
||||||
|
default=env('OS_TENANT_ID'),
|
||||||
|
help=_('Authentication tenant ID, defaults to '
|
||||||
|
'env[OS_TENANT_ID].'))
|
||||||
|
project_id_group.add_argument(
|
||||||
|
'--os-project-id',
|
||||||
|
metavar='<auth-project-id>',
|
||||||
|
default=utils.env('OS_PROJECT_ID'),
|
||||||
|
help='Another way to specify tenant ID. '
|
||||||
|
'This option is mutually exclusive with '
|
||||||
|
' --os-tenant-id. '
|
||||||
|
'Defaults to env[OS_PROJECT_ID].')
|
||||||
|
|
||||||
|
parser.add_argument(
|
||||||
|
'--os-username', metavar='<auth-username>',
|
||||||
|
default=utils.env('OS_USERNAME'),
|
||||||
|
help=_('Authentication username, defaults to env[OS_USERNAME].'))
|
||||||
|
parser.add_argument(
|
||||||
|
'--os_username',
|
||||||
|
help=argparse.SUPPRESS)
|
||||||
|
|
||||||
|
parser.add_argument(
|
||||||
|
'--os-user-id', metavar='<auth-user-id>',
|
||||||
|
default=env('OS_USER_ID'),
|
||||||
|
help=_('Authentication user ID (Env: OS_USER_ID)'))
|
||||||
|
|
||||||
|
parser.add_argument(
|
||||||
|
'--os_user_id',
|
||||||
|
help=argparse.SUPPRESS)
|
||||||
|
|
||||||
|
parser.add_argument(
|
||||||
|
'--os-user-domain-id',
|
||||||
|
metavar='<auth-user-domain-id>',
|
||||||
|
default=utils.env('OS_USER_DOMAIN_ID'),
|
||||||
|
help='OpenStack user domain ID. '
|
||||||
|
'Defaults to env[OS_USER_DOMAIN_ID].')
|
||||||
|
|
||||||
|
parser.add_argument(
|
||||||
|
'--os_user_domain_id',
|
||||||
|
help=argparse.SUPPRESS)
|
||||||
|
|
||||||
|
parser.add_argument(
|
||||||
|
'--os-user-domain-name',
|
||||||
|
metavar='<auth-user-domain-name>',
|
||||||
|
default=utils.env('OS_USER_DOMAIN_NAME'),
|
||||||
|
help='OpenStack user domain name. '
|
||||||
|
'Defaults to env[OS_USER_DOMAIN_NAME].')
|
||||||
|
|
||||||
|
parser.add_argument(
|
||||||
|
'--os_user_domain_name',
|
||||||
|
help=argparse.SUPPRESS)
|
||||||
|
|
||||||
|
parser.add_argument(
|
||||||
|
'--os_project_id',
|
||||||
|
help=argparse.SUPPRESS)
|
||||||
|
|
||||||
|
parser.add_argument(
|
||||||
|
'--os_project_name',
|
||||||
|
help=argparse.SUPPRESS)
|
||||||
|
|
||||||
|
parser.add_argument(
|
||||||
|
'--os-project-domain-id',
|
||||||
|
metavar='<auth-project-domain-id>',
|
||||||
|
default=utils.env('OS_PROJECT_DOMAIN_ID'),
|
||||||
|
help='Defaults to env[OS_PROJECT_DOMAIN_ID].')
|
||||||
|
|
||||||
|
parser.add_argument(
|
||||||
|
'--os-project-domain-name',
|
||||||
|
metavar='<auth-project-domain-name>',
|
||||||
|
default=utils.env('OS_PROJECT_DOMAIN_NAME'),
|
||||||
|
help='Defaults to env[OS_PROJECT_DOMAIN_NAME].')
|
||||||
|
|
||||||
|
parser.add_argument(
|
||||||
|
'--os-cert',
|
||||||
|
metavar='<certificate>',
|
||||||
|
default=utils.env('OS_CERT'),
|
||||||
|
help=_("Path of certificate file to use in SSL "
|
||||||
|
"connection. This file can optionally be "
|
||||||
|
"prepended with the private key. Defaults "
|
||||||
|
"to env[OS_CERT]"))
|
||||||
|
|
||||||
|
parser.add_argument(
|
||||||
|
'--os-cacert',
|
||||||
|
metavar='<ca-certificate>',
|
||||||
|
default=env('OS_CACERT', default=None),
|
||||||
|
help=_("Specify a CA bundle file to use in "
|
||||||
|
"verifying a TLS (https) server certificate. "
|
||||||
|
"Defaults to env[OS_CACERT]"))
|
||||||
|
|
||||||
|
parser.add_argument(
|
||||||
|
'--os-key',
|
||||||
|
metavar='<key>',
|
||||||
|
default=utils.env('OS_KEY'),
|
||||||
|
help=_("Path of client key to use in SSL "
|
||||||
|
"connection. This option is not necessary "
|
||||||
|
"if your key is prepended to your certificate "
|
||||||
|
"file. Defaults to env[OS_KEY]"))
|
||||||
|
|
||||||
|
parser.add_argument(
|
||||||
|
'--os-password', metavar='<auth-password>',
|
||||||
|
default=utils.env('OS_PASSWORD'),
|
||||||
|
help=_('Authentication password, defaults to env[OS_PASSWORD].'))
|
||||||
|
parser.add_argument(
|
||||||
|
'--os_password',
|
||||||
|
help=argparse.SUPPRESS)
|
||||||
|
|
||||||
|
parser.add_argument(
|
||||||
|
'--os-region-name', metavar='<auth-region-name>',
|
||||||
|
default=env('OS_REGION_NAME'),
|
||||||
|
help=_('Authentication region name, defaults to '
|
||||||
|
'env[OS_REGION_NAME].'))
|
||||||
|
parser.add_argument(
|
||||||
|
'--os_region_name',
|
||||||
|
help=argparse.SUPPRESS)
|
||||||
|
|
||||||
|
parser.add_argument(
|
||||||
|
'--os-token', metavar='<token>',
|
||||||
|
default=env('OS_TOKEN'),
|
||||||
|
help=_('Authentication token, defaults to env[OS_TOKEN].'))
|
||||||
|
parser.add_argument(
|
||||||
|
'--os_token',
|
||||||
|
help=argparse.SUPPRESS)
|
||||||
|
|
||||||
|
parser.add_argument(
|
||||||
|
'--http-timeout', metavar='<seconds>',
|
||||||
|
default=env('OS_NETWORK_TIMEOUT', default=None), type=float,
|
||||||
|
help=_('Timeout in seconds to wait for an HTTP response. Defaults '
|
||||||
|
'to env[OS_NETWORK_TIMEOUT] or None if not specified.'))
|
||||||
|
|
||||||
|
parser.add_argument(
|
||||||
|
'--os-url', metavar='<url>',
|
||||||
|
default=env('OS_URL'),
|
||||||
|
help=_('Defaults to env[OS_URL].'))
|
||||||
|
parser.add_argument(
|
||||||
|
'--os_url',
|
||||||
|
help=argparse.SUPPRESS)
|
||||||
|
|
||||||
|
parser.add_argument(
|
||||||
|
'--insecure',
|
||||||
|
action='store_true',
|
||||||
|
default=env('NEUTRONCLIENT_INSECURE', default=False),
|
||||||
|
help=_("Explicitly allow neutronclient to perform \"insecure\" "
|
||||||
|
"SSL (https) requests. The server's certificate will "
|
||||||
|
"not be verified against any certificate authorities. "
|
||||||
|
"This option should be used with caution."))
|
||||||
|
|
||||||
|
def _bash_completion(self):
|
||||||
|
"""Prints all of the commands and options for bash-completion."""
|
||||||
|
commands = set()
|
||||||
|
options = set()
|
||||||
|
for option, _action in self.parser._option_string_actions.items():
|
||||||
|
options.add(option)
|
||||||
|
for command_name, command in self.command_manager:
|
||||||
|
commands.add(command_name)
|
||||||
|
cmd_factory = command.load()
|
||||||
|
cmd = cmd_factory(self, None)
|
||||||
|
cmd_parser = cmd.get_parser('')
|
||||||
|
for option, _action in cmd_parser._option_string_actions.items():
|
||||||
|
options.add(option)
|
||||||
|
print(' '.join(commands | options))
|
||||||
|
|
||||||
|
def run(self, argv):
|
||||||
|
"""Equivalent to the main program for the application.
|
||||||
|
|
||||||
|
:param argv: input arguments and options
|
||||||
|
:paramtype argv: list of str
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
index = 0
|
||||||
|
command_pos = -1
|
||||||
|
help_pos = -1
|
||||||
|
help_command_pos = -1
|
||||||
|
for arg in argv:
|
||||||
|
if arg == 'bash-completion':
|
||||||
|
self._bash_completion()
|
||||||
|
return 0
|
||||||
|
if arg in self.commands[self.api_version]:
|
||||||
|
if command_pos == -1:
|
||||||
|
command_pos = index
|
||||||
|
elif arg in ('-h', '--help'):
|
||||||
|
if help_pos == -1:
|
||||||
|
help_pos = index
|
||||||
|
elif arg == 'help':
|
||||||
|
if help_command_pos == -1:
|
||||||
|
help_command_pos = index
|
||||||
|
index = index + 1
|
||||||
|
if command_pos > -1 and help_pos > command_pos:
|
||||||
|
argv = ['help', argv[command_pos]]
|
||||||
|
if help_command_pos > -1 and command_pos == -1:
|
||||||
|
argv[help_command_pos] = '--help'
|
||||||
|
self.options, remainder = self.parser.parse_known_args(argv)
|
||||||
|
self.configure_logging()
|
||||||
|
self.interactive_mode = not remainder
|
||||||
|
self.initialize_app(remainder)
|
||||||
|
except Exception as err:
|
||||||
|
if self.options.verbose_level >= self.DEBUG_LEVEL:
|
||||||
|
self.log.exception(unicode(err))
|
||||||
|
raise
|
||||||
|
else:
|
||||||
|
self.log.error(unicode(err))
|
||||||
|
return 1
|
||||||
|
result = 1
|
||||||
|
if self.interactive_mode:
|
||||||
|
_argv = [sys.argv[0]]
|
||||||
|
sys.argv = _argv
|
||||||
|
result = self.interact()
|
||||||
|
else:
|
||||||
|
result = self.run_subcommand(remainder)
|
||||||
|
return result
|
||||||
|
|
||||||
|
def run_subcommand(self, argv):
|
||||||
|
subcommand = self.command_manager.find_command(argv)
|
||||||
|
cmd_factory, cmd_name, sub_argv = subcommand
|
||||||
|
cmd = cmd_factory(self, self.options)
|
||||||
|
err = None
|
||||||
|
result = 1
|
||||||
|
try:
|
||||||
|
self.prepare_to_run_command(cmd)
|
||||||
|
full_name = (cmd_name
|
||||||
|
if self.interactive_mode
|
||||||
|
else ' '.join([self.NAME, cmd_name])
|
||||||
|
)
|
||||||
|
cmd_parser = cmd.get_parser(full_name)
|
||||||
|
return run_command(cmd, cmd_parser, sub_argv)
|
||||||
|
except Exception as err:
|
||||||
|
if self.options.verbose_level >= self.DEBUG_LEVEL:
|
||||||
|
self.log.exception(unicode(err))
|
||||||
|
else:
|
||||||
|
self.log.error(unicode(err))
|
||||||
|
try:
|
||||||
|
self.clean_up(cmd, result, err)
|
||||||
|
except Exception as err2:
|
||||||
|
if self.options.verbose_level >= self.DEBUG_LEVEL:
|
||||||
|
self.log.exception(unicode(err2))
|
||||||
|
else:
|
||||||
|
self.log.error(_('Could not clean up: %s'), unicode(err2))
|
||||||
|
if self.options.verbose_level >= self.DEBUG_LEVEL:
|
||||||
|
raise
|
||||||
|
else:
|
||||||
|
try:
|
||||||
|
self.clean_up(cmd, result, None)
|
||||||
|
except Exception as err3:
|
||||||
|
if self.options.verbose_level >= self.DEBUG_LEVEL:
|
||||||
|
self.log.exception(unicode(err3))
|
||||||
|
else:
|
||||||
|
self.log.error(_('Could not clean up: %s'), unicode(err3))
|
||||||
|
return result
|
||||||
|
|
||||||
|
def authenticate_user(self):
|
||||||
|
"""Make sure the user has provided all of the authentication
|
||||||
|
info we need.
|
||||||
|
"""
|
||||||
|
if self.options.os_auth_strategy == 'keystone':
|
||||||
|
if self.options.os_token or self.options.os_url:
|
||||||
|
# Token flow auth takes priority
|
||||||
|
if not self.options.os_token:
|
||||||
|
raise exc.CommandError(
|
||||||
|
_("You must provide a token via"
|
||||||
|
" either --os-token or env[OS_TOKEN]"))
|
||||||
|
|
||||||
|
if not self.options.os_url:
|
||||||
|
raise exc.CommandError(
|
||||||
|
_("You must provide a service URL via"
|
||||||
|
" either --os-url or env[OS_URL]"))
|
||||||
|
|
||||||
|
else:
|
||||||
|
# Validate password flow auth
|
||||||
|
project_info = (self.options.os_tenant_name or
|
||||||
|
self.options.os_tenant_id or
|
||||||
|
(self.options.os_project_name and
|
||||||
|
(self.options.project_domain_name or
|
||||||
|
self.options.project_domain_id)) or
|
||||||
|
self.options.os_project_id)
|
||||||
|
|
||||||
|
if (not self.options.os_username
|
||||||
|
and not self.options.os_user_id):
|
||||||
|
raise exc.CommandError(
|
||||||
|
_("You must provide a username or user ID via"
|
||||||
|
" --os-username, env[OS_USERNAME] or"
|
||||||
|
" --os-user_id, env[OS_USER_ID]"))
|
||||||
|
|
||||||
|
if not self.options.os_password:
|
||||||
|
raise exc.CommandError(
|
||||||
|
_("You must provide a password via"
|
||||||
|
" either --os-password or env[OS_PASSWORD]"))
|
||||||
|
|
||||||
|
if (not project_info):
|
||||||
|
# tenent is deprecated in Keystone v3. Use the latest
|
||||||
|
# terminology instead.
|
||||||
|
raise exc.CommandError(
|
||||||
|
_("You must provide a project_id or project_name ("
|
||||||
|
"with project_domain_name or project_domain_id) "
|
||||||
|
"via "
|
||||||
|
" --os-project-id (env[OS_PROJECT_ID])"
|
||||||
|
" --os-project-name (env[OS_PROJECT_NAME]),"
|
||||||
|
" --os-project-domain-id "
|
||||||
|
"(env[OS_PROJECT_DOMAIN_ID])"
|
||||||
|
" --os-project-domain-name "
|
||||||
|
"(env[OS_PROJECT_DOMAIN_NAME])"))
|
||||||
|
|
||||||
|
if not self.options.os_auth_url:
|
||||||
|
raise exc.CommandError(
|
||||||
|
_("You must provide an auth url via"
|
||||||
|
" either --os-auth-url or via env[OS_AUTH_URL]"))
|
||||||
|
else: # not keystone
|
||||||
|
if not self.options.os_url:
|
||||||
|
raise exc.CommandError(
|
||||||
|
_("You must provide a service URL via"
|
||||||
|
" either --os-url or env[OS_URL]"))
|
||||||
|
|
||||||
|
auth_session = self._get_keystone_session()
|
||||||
|
|
||||||
|
self.client_manager = clientmanager.ClientManager(
|
||||||
|
token=self.options.os_token,
|
||||||
|
url=self.options.os_url,
|
||||||
|
auth_url=self.options.os_auth_url,
|
||||||
|
tenant_name=self.options.os_tenant_name,
|
||||||
|
tenant_id=self.options.os_tenant_id,
|
||||||
|
username=self.options.os_username,
|
||||||
|
user_id=self.options.os_user_id,
|
||||||
|
password=self.options.os_password,
|
||||||
|
region_name=self.options.os_region_name,
|
||||||
|
api_version=self.api_version,
|
||||||
|
auth_strategy=self.options.os_auth_strategy,
|
||||||
|
# FIXME (bklei) honor deprecated service_type and
|
||||||
|
# endpoint type until they are removed
|
||||||
|
service_type=self.options.os_service_type or
|
||||||
|
self.options.service_type,
|
||||||
|
endpoint_type=self.options.os_endpoint_type or self.endpoint_type,
|
||||||
|
insecure=self.options.insecure,
|
||||||
|
ca_cert=self.options.os_cacert,
|
||||||
|
timeout=self.options.http_timeout,
|
||||||
|
retries=self.options.retries,
|
||||||
|
raise_errors=False,
|
||||||
|
session=auth_session,
|
||||||
|
auth=auth_session.auth,
|
||||||
|
log_credentials=True)
|
||||||
|
return
|
||||||
|
|
||||||
|
def initialize_app(self, argv):
|
||||||
|
"""Global app init bits:
|
||||||
|
|
||||||
|
* set up API versions
|
||||||
|
* validate authentication info
|
||||||
|
"""
|
||||||
|
|
||||||
|
super(GBPShell, self).initialize_app(argv)
|
||||||
|
|
||||||
|
self.api_version = {'network': self.api_version}
|
||||||
|
|
||||||
|
# If the user is not asking for help, make sure they
|
||||||
|
# have given us auth.
|
||||||
|
cmd_name = None
|
||||||
|
if argv:
|
||||||
|
cmd_info = self.command_manager.find_command(argv)
|
||||||
|
cmd_factory, cmd_name, sub_argv = cmd_info
|
||||||
|
if self.interactive_mode or cmd_name != 'help':
|
||||||
|
self.authenticate_user()
|
||||||
|
|
||||||
|
def clean_up(self, cmd, result, err):
|
||||||
|
self.log.debug('clean_up %s', cmd.__class__.__name__)
|
||||||
|
if err:
|
||||||
|
self.log.debug('Got an error: %s', unicode(err))
|
||||||
|
|
||||||
|
def configure_logging(self):
|
||||||
|
"""Create logging handlers for any log output."""
|
||||||
|
root_logger = logging.getLogger('')
|
||||||
|
|
||||||
|
# Set up logging to a file
|
||||||
|
root_logger.setLevel(logging.DEBUG)
|
||||||
|
|
||||||
|
# Send higher-level messages to the console via stderr
|
||||||
|
console = logging.StreamHandler(self.stderr)
|
||||||
|
console_level = {self.WARNING_LEVEL: logging.WARNING,
|
||||||
|
self.INFO_LEVEL: logging.INFO,
|
||||||
|
self.DEBUG_LEVEL: logging.DEBUG,
|
||||||
|
}.get(self.options.verbose_level, logging.DEBUG)
|
||||||
|
console.setLevel(console_level)
|
||||||
|
if logging.DEBUG == console_level:
|
||||||
|
formatter = logging.Formatter(self.DEBUG_MESSAGE_FORMAT)
|
||||||
|
else:
|
||||||
|
formatter = logging.Formatter(self.CONSOLE_MESSAGE_FORMAT)
|
||||||
|
logging.getLogger('urllib3.connectionpool').setLevel(logging.WARNING)
|
||||||
|
console.setFormatter(formatter)
|
||||||
|
root_logger.addHandler(console)
|
||||||
|
return
|
||||||
|
|
||||||
|
def get_v2_auth(self, v2_auth_url):
|
||||||
|
return v2_auth.Password(
|
||||||
|
v2_auth_url,
|
||||||
|
username=self.options.os_username,
|
||||||
|
password=self.options.os_password,
|
||||||
|
tenant_id=self.options.os_tenant_id,
|
||||||
|
tenant_name=self.options.os_tenant_name)
|
||||||
|
|
||||||
|
def get_v3_auth(self, v3_auth_url):
|
||||||
|
project_id = self.options.os_project_id or self.options.os_tenant_id
|
||||||
|
project_name = (self.options.os_project_name or
|
||||||
|
self.options.os_tenant_name)
|
||||||
|
|
||||||
|
return v3_auth.Password(
|
||||||
|
v3_auth_url,
|
||||||
|
username=self.options.os_username,
|
||||||
|
password=self.options.os_password,
|
||||||
|
user_id=self.options.os_user_id,
|
||||||
|
user_domain_name=self.options.os_user_domain_name,
|
||||||
|
user_domain_id=self.options.os_user_domain_id,
|
||||||
|
project_id=project_id,
|
||||||
|
project_name=project_name,
|
||||||
|
project_domain_name=self.options.os_project_domain_name,
|
||||||
|
project_domain_id=self.options.os_project_domain_id
|
||||||
|
)
|
||||||
|
|
||||||
|
def _discover_auth_versions(self, session, auth_url):
|
||||||
|
# discover the API versions the server is supporting base on the
|
||||||
|
# given URL
|
||||||
|
try:
|
||||||
|
ks_discover = discover.Discover(session=session, auth_url=auth_url)
|
||||||
|
return (ks_discover.url_for('2.0'), ks_discover.url_for('3.0'))
|
||||||
|
except ks_exc.ClientException:
|
||||||
|
# Identity service may not support discover API version.
|
||||||
|
# Lets try to figure out the API version from the original URL.
|
||||||
|
url_parts = urlparse.urlparse(auth_url)
|
||||||
|
(scheme, netloc, path, params, query, fragment) = url_parts
|
||||||
|
path = path.lower()
|
||||||
|
if path.startswith('/v3'):
|
||||||
|
return (None, auth_url)
|
||||||
|
elif path.startswith('/v2'):
|
||||||
|
return (auth_url, None)
|
||||||
|
else:
|
||||||
|
# not enough information to determine the auth version
|
||||||
|
msg = _('Unable to determine the Keystone version '
|
||||||
|
'to authenticate with using the given '
|
||||||
|
'auth_url. Identity service may not support API '
|
||||||
|
'version discovery. Please provide a versioned '
|
||||||
|
'auth_url instead.')
|
||||||
|
raise exc.CommandError(msg)
|
||||||
|
|
||||||
|
def _get_keystone_session(self):
|
||||||
|
# first create a Keystone session
|
||||||
|
cacert = self.options.os_cacert or None
|
||||||
|
cert = self.options.os_cert or None
|
||||||
|
key = self.options.os_key or None
|
||||||
|
insecure = self.options.insecure or False
|
||||||
|
ks_session = session.Session.construct(dict(cacert=cacert,
|
||||||
|
cert=cert,
|
||||||
|
key=key,
|
||||||
|
insecure=insecure))
|
||||||
|
# discover the supported keystone versions using the given url
|
||||||
|
(v2_auth_url, v3_auth_url) = self._discover_auth_versions(
|
||||||
|
session=ks_session,
|
||||||
|
auth_url=self.options.os_auth_url)
|
||||||
|
|
||||||
|
# Determine which authentication plugin to use. First inspect the
|
||||||
|
# auth_url to see the supported version. If both v3 and v2 are
|
||||||
|
# supported, then use the highest version if possible.
|
||||||
|
user_domain_name = self.options.os_user_domain_name or None
|
||||||
|
user_domain_id = self.options.os_user_domain_id or None
|
||||||
|
project_domain_name = self.options.os_project_domain_name or None
|
||||||
|
project_domain_id = self.options.os_project_domain_id or None
|
||||||
|
domain_info = (user_domain_name or user_domain_id or
|
||||||
|
project_domain_name or project_domain_id)
|
||||||
|
|
||||||
|
if (v2_auth_url and not domain_info) or not v3_auth_url:
|
||||||
|
ks_session.auth = self.get_v2_auth(v2_auth_url)
|
||||||
|
else:
|
||||||
|
ks_session.auth = self.get_v3_auth(v3_auth_url)
|
||||||
|
|
||||||
|
return ks_session
|
||||||
|
|
||||||
|
|
||||||
|
def main(argv=sys.argv[1:]):
|
||||||
|
try:
|
||||||
|
return GBPShell(NEUTRON_API_VERSION).run(map(strutils.safe_decode,
|
||||||
|
argv))
|
||||||
|
except exc.NeutronClientException:
|
||||||
|
return 1
|
||||||
|
except Exception as e:
|
||||||
|
print(unicode(e))
|
||||||
|
return 1
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
sys.exit(main(sys.argv[1:]))
|
@ -30,7 +30,6 @@ from keystoneclient import exceptions as ks_exceptions
|
|||||||
from keystoneclient.fixture import v2 as ks_v2_fixture
|
from keystoneclient.fixture import v2 as ks_v2_fixture
|
||||||
from keystoneclient.fixture import v3 as ks_v3_fixture
|
from keystoneclient.fixture import v3 as ks_v3_fixture
|
||||||
from keystoneclient import session
|
from keystoneclient import session
|
||||||
|
|
||||||
from neutronclient import client
|
from neutronclient import client
|
||||||
from neutronclient.common import exceptions
|
from neutronclient.common import exceptions
|
||||||
from neutronclient.common import utils
|
from neutronclient.common import utils
|
||||||
|
@ -1,6 +1,3 @@
|
|||||||
# Copyright 2012 OpenStack Foundation.
|
|
||||||
# All Rights Reserved
|
|
||||||
#
|
|
||||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
# 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
|
# not use this file except in compliance with the License. You may obtain
|
||||||
# a copy of the License at
|
# a copy of the License at
|
||||||
@ -14,198 +11,56 @@
|
|||||||
# under the License.
|
# under the License.
|
||||||
#
|
#
|
||||||
|
|
||||||
import contextlib
|
|
||||||
import itertools
|
|
||||||
import sys
|
|
||||||
import urllib
|
|
||||||
|
|
||||||
import fixtures
|
|
||||||
from mox3 import mox
|
from mox3 import mox
|
||||||
from oslotest import base
|
|
||||||
import requests
|
|
||||||
import six
|
|
||||||
import six.moves.urllib.parse as urlparse
|
|
||||||
|
|
||||||
from neutronclient.common import constants
|
|
||||||
from neutronclient.common import exceptions
|
from neutronclient.common import exceptions
|
||||||
from neutronclient.neutron import v2_0 as neutronV2_0
|
from neutronclient.tests.unit import test_cli20 as neutron_test_cli20
|
||||||
from neutronclient import shell
|
import requests
|
||||||
from neutronclient.v2_0 import client
|
|
||||||
|
|
||||||
API_VERSION = "2.0"
|
from gbpclient.gbp import v2_0 as gbpV2_0
|
||||||
FORMAT = 'json'
|
from gbpclient import gbpshell
|
||||||
TOKEN = 'testtoken'
|
from gbpclient.v2_0 import client as gbpclient
|
||||||
ENDURL = 'localurl'
|
|
||||||
|
API_VERSION = neutron_test_cli20.API_VERSION
|
||||||
|
FORMAT = neutron_test_cli20.FORMAT
|
||||||
|
TOKEN = neutron_test_cli20.TOKEN
|
||||||
|
ENDURL = neutron_test_cli20.ENDURL
|
||||||
|
capture_std_streams = neutron_test_cli20.capture_std_streams
|
||||||
|
end_url = neutron_test_cli20.end_url
|
||||||
|
|
||||||
|
|
||||||
@contextlib.contextmanager
|
class FakeStdout(neutron_test_cli20.FakeStdout):
|
||||||
def capture_std_streams():
|
|
||||||
fake_stdout, fake_stderr = six.StringIO(), six.StringIO()
|
pass
|
||||||
stdout, stderr = sys.stdout, sys.stderr
|
|
||||||
try:
|
|
||||||
sys.stdout, sys.stderr = fake_stdout, fake_stderr
|
|
||||||
yield fake_stdout, fake_stderr
|
|
||||||
finally:
|
|
||||||
sys.stdout, sys.stderr = stdout, stderr
|
|
||||||
|
|
||||||
|
|
||||||
class FakeStdout:
|
class MyResp(neutron_test_cli20.MyResp):
|
||||||
|
|
||||||
def __init__(self):
|
pass
|
||||||
self.content = []
|
|
||||||
|
|
||||||
def write(self, text):
|
|
||||||
self.content.append(text)
|
|
||||||
|
|
||||||
def make_string(self):
|
|
||||||
result = ''
|
|
||||||
for line in self.content:
|
|
||||||
result = result + line
|
|
||||||
return result
|
|
||||||
|
|
||||||
|
|
||||||
class MyResp(object):
|
class MyApp(neutron_test_cli20.MyApp):
|
||||||
def __init__(self, status_code, headers=None, reason=None):
|
|
||||||
self.status_code = status_code
|
pass
|
||||||
self.headers = headers or {}
|
|
||||||
self.reason = reason
|
|
||||||
|
|
||||||
|
|
||||||
class MyApp(object):
|
class MyUrlComparator(neutron_test_cli20.MyUrlComparator):
|
||||||
def __init__(self, _stdout):
|
|
||||||
self.stdout = _stdout
|
pass
|
||||||
|
|
||||||
|
|
||||||
def end_url(path, query=None, format=FORMAT):
|
class MyComparator(neutron_test_cli20.MyComparator):
|
||||||
_url_str = ENDURL + "/v" + API_VERSION + path + "." + format
|
|
||||||
return query and _url_str + "?" + query or _url_str
|
pass
|
||||||
|
|
||||||
|
|
||||||
class MyUrlComparator(mox.Comparator):
|
class CLITestV20Base(neutron_test_cli20.CLITestV20Base):
|
||||||
def __init__(self, lhs, client):
|
|
||||||
self.lhs = lhs
|
|
||||||
self.client = client
|
|
||||||
|
|
||||||
def equals(self, rhs):
|
shell = gbpshell
|
||||||
lhsp = urlparse.urlparse(self.lhs)
|
client = gbpclient
|
||||||
rhsp = urlparse.urlparse(rhs)
|
|
||||||
|
|
||||||
return (lhsp.scheme == rhsp.scheme and
|
|
||||||
lhsp.netloc == rhsp.netloc and
|
|
||||||
lhsp.path == rhsp.path and
|
|
||||||
urlparse.parse_qs(lhsp.query) == urlparse.parse_qs(rhsp.query))
|
|
||||||
|
|
||||||
def __str__(self):
|
|
||||||
if self.client and self.client.format != FORMAT:
|
|
||||||
lhs_parts = self.lhs.split("?", 1)
|
|
||||||
if len(lhs_parts) == 2:
|
|
||||||
lhs = ("%s.%s?%s" % (lhs_parts[0][:-4],
|
|
||||||
self.client.format,
|
|
||||||
lhs_parts[1]))
|
|
||||||
else:
|
|
||||||
lhs = ("%s.%s" % (lhs_parts[0][:-4],
|
|
||||||
self.client.format))
|
|
||||||
return lhs
|
|
||||||
return self.lhs
|
|
||||||
|
|
||||||
def __repr__(self):
|
|
||||||
return str(self)
|
|
||||||
|
|
||||||
|
|
||||||
class MyComparator(mox.Comparator):
|
|
||||||
def __init__(self, lhs, client):
|
|
||||||
self.lhs = lhs
|
|
||||||
self.client = client
|
|
||||||
|
|
||||||
def _com_dict(self, lhs, rhs):
|
|
||||||
if len(lhs) != len(rhs):
|
|
||||||
return False
|
|
||||||
for key, value in six.iteritems(lhs):
|
|
||||||
if key not in rhs:
|
|
||||||
return False
|
|
||||||
rhs_value = rhs[key]
|
|
||||||
if not self._com(value, rhs_value):
|
|
||||||
return False
|
|
||||||
return True
|
|
||||||
|
|
||||||
def _com_list(self, lhs, rhs):
|
|
||||||
if len(lhs) != len(rhs):
|
|
||||||
return False
|
|
||||||
for lhs_value in lhs:
|
|
||||||
if lhs_value not in rhs:
|
|
||||||
return False
|
|
||||||
return True
|
|
||||||
|
|
||||||
def _com(self, lhs, rhs):
|
|
||||||
if lhs is None:
|
|
||||||
return rhs is None
|
|
||||||
if isinstance(lhs, dict):
|
|
||||||
if not isinstance(rhs, dict):
|
|
||||||
return False
|
|
||||||
return self._com_dict(lhs, rhs)
|
|
||||||
if isinstance(lhs, list):
|
|
||||||
if not isinstance(rhs, list):
|
|
||||||
return False
|
|
||||||
return self._com_list(lhs, rhs)
|
|
||||||
if isinstance(lhs, tuple):
|
|
||||||
if not isinstance(rhs, tuple):
|
|
||||||
return False
|
|
||||||
return self._com_list(lhs, rhs)
|
|
||||||
return lhs == rhs
|
|
||||||
|
|
||||||
def equals(self, rhs):
|
|
||||||
if self.client:
|
|
||||||
rhs = self.client.deserialize(rhs, 200)
|
|
||||||
return self._com(self.lhs, rhs)
|
|
||||||
|
|
||||||
def __repr__(self):
|
|
||||||
if self.client:
|
|
||||||
return self.client.serialize(self.lhs)
|
|
||||||
return str(self.lhs)
|
|
||||||
|
|
||||||
|
|
||||||
class CLITestV20Base(base.BaseTestCase):
|
|
||||||
|
|
||||||
format = 'json'
|
|
||||||
test_id = 'aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa'
|
|
||||||
id_field = 'id'
|
|
||||||
|
|
||||||
def _find_resourceid(self, client, resource, name_or_id,
|
|
||||||
cmd_resource=None, parent_id=None):
|
|
||||||
return name_or_id
|
|
||||||
|
|
||||||
def _get_attr_metadata(self):
|
|
||||||
return self.metadata
|
|
||||||
client.Client.EXTED_PLURALS.update(constants.PLURALS)
|
|
||||||
client.Client.EXTED_PLURALS.update({'tags': 'tag'})
|
|
||||||
return {'plurals': client.Client.EXTED_PLURALS,
|
|
||||||
'xmlns': constants.XML_NS_V20,
|
|
||||||
constants.EXT_NS: {'prefix': 'http://xxxx.yy.com'}}
|
|
||||||
|
|
||||||
def setUp(self, plurals=None):
|
def setUp(self, plurals=None):
|
||||||
"""Prepare the test environment."""
|
|
||||||
super(CLITestV20Base, self).setUp()
|
super(CLITestV20Base, self).setUp()
|
||||||
client.Client.EXTED_PLURALS.update(constants.PLURALS)
|
self.client = gbpclient.Client(token=TOKEN, endpoint_url=self.endurl)
|
||||||
if plurals is not None:
|
|
||||||
client.Client.EXTED_PLURALS.update(plurals)
|
|
||||||
self.metadata = {'plurals': client.Client.EXTED_PLURALS,
|
|
||||||
'xmlns': constants.XML_NS_V20,
|
|
||||||
constants.EXT_NS: {'prefix':
|
|
||||||
'http://xxxx.yy.com'}}
|
|
||||||
self.mox = mox.Mox()
|
|
||||||
self.endurl = ENDURL
|
|
||||||
self.fake_stdout = FakeStdout()
|
|
||||||
self.useFixture(fixtures.MonkeyPatch('sys.stdout', self.fake_stdout))
|
|
||||||
self.useFixture(fixtures.MonkeyPatch(
|
|
||||||
'neutronclient.neutron.v2_0.find_resourceid_by_name_or_id',
|
|
||||||
self._find_resourceid))
|
|
||||||
self.useFixture(fixtures.MonkeyPatch(
|
|
||||||
'neutronclient.neutron.v2_0.find_resourceid_by_id',
|
|
||||||
self._find_resourceid))
|
|
||||||
self.useFixture(fixtures.MonkeyPatch(
|
|
||||||
'neutronclient.v2_0.client.Client.get_attr_metadata',
|
|
||||||
self._get_attr_metadata))
|
|
||||||
self.client = client.Client(token=TOKEN, endpoint_url=self.endurl)
|
|
||||||
|
|
||||||
def _test_create_resource(self, resource, cmd, name, myid, args,
|
def _test_create_resource(self, resource, cmd, name, myid, args,
|
||||||
position_names, position_values,
|
position_names, position_values,
|
||||||
@ -215,19 +70,9 @@ class CLITestV20Base(base.BaseTestCase):
|
|||||||
self.mox.StubOutWithMock(cmd, "get_client")
|
self.mox.StubOutWithMock(cmd, "get_client")
|
||||||
self.mox.StubOutWithMock(self.client.httpclient, "request")
|
self.mox.StubOutWithMock(self.client.httpclient, "request")
|
||||||
cmd.get_client().MultipleTimes().AndReturn(self.client)
|
cmd.get_client().MultipleTimes().AndReturn(self.client)
|
||||||
non_admin_status_resources = ['subnet', 'floatingip', 'security_group',
|
|
||||||
'security_group_rule', 'qos_queue',
|
|
||||||
'network_gateway', 'gateway_device',
|
|
||||||
'credential', 'network_profile',
|
|
||||||
'policy_profile', 'ikepolicy',
|
|
||||||
'ipsecpolicy', 'metering_label',
|
|
||||||
'metering_label_rule', 'net_partition']
|
|
||||||
if not cmd_resource:
|
if not cmd_resource:
|
||||||
cmd_resource = resource
|
cmd_resource = resource
|
||||||
if (resource in non_admin_status_resources):
|
|
||||||
body = {resource: {}, }
|
body = {resource: {}, }
|
||||||
else:
|
|
||||||
body = {resource: {'admin_state_up': admin_state_up, }, }
|
|
||||||
if tenant_id:
|
if tenant_id:
|
||||||
body[resource].update({'tenant_id': tenant_id})
|
body[resource].update({'tenant_id': tenant_id})
|
||||||
if tags:
|
if tags:
|
||||||
@ -245,7 +90,7 @@ class CLITestV20Base(base.BaseTestCase):
|
|||||||
self.client.format = self.format
|
self.client.format = self.format
|
||||||
resstr = self.client.serialize(ress)
|
resstr = self.client.serialize(ress)
|
||||||
# url method body
|
# url method body
|
||||||
resource_plural = neutronV2_0._get_resource_plural(cmd_resource,
|
resource_plural = gbpV2_0._get_resource_plural(cmd_resource,
|
||||||
self.client)
|
self.client)
|
||||||
path = getattr(self.client, resource_plural + "_path")
|
path = getattr(self.client, resource_plural + "_path")
|
||||||
if parent_id:
|
if parent_id:
|
||||||
@ -264,7 +109,7 @@ class CLITestV20Base(base.BaseTestCase):
|
|||||||
args.extend(['--request-format', self.format])
|
args.extend(['--request-format', self.format])
|
||||||
self.mox.ReplayAll()
|
self.mox.ReplayAll()
|
||||||
cmd_parser = cmd.get_parser('create_' + resource)
|
cmd_parser = cmd.get_parser('create_' + resource)
|
||||||
shell.run_command(cmd, cmd_parser, args)
|
gbpshell.run_command(cmd, cmd_parser, args)
|
||||||
self.mox.VerifyAll()
|
self.mox.VerifyAll()
|
||||||
self.mox.UnsetStubs()
|
self.mox.UnsetStubs()
|
||||||
_str = self.fake_stdout.make_string()
|
_str = self.fake_stdout.make_string()
|
||||||
@ -272,354 +117,10 @@ class CLITestV20Base(base.BaseTestCase):
|
|||||||
if name:
|
if name:
|
||||||
self.assertIn(name, _str)
|
self.assertIn(name, _str)
|
||||||
|
|
||||||
def _test_list_columns(self, cmd, resources,
|
|
||||||
resources_out, args=('-f', 'json'),
|
|
||||||
cmd_resources=None, parent_id=None):
|
|
||||||
self.mox.StubOutWithMock(cmd, "get_client")
|
|
||||||
self.mox.StubOutWithMock(self.client.httpclient, "request")
|
|
||||||
cmd.get_client().MultipleTimes().AndReturn(self.client)
|
|
||||||
self.client.format = self.format
|
|
||||||
if not cmd_resources:
|
|
||||||
cmd_resources = resources
|
|
||||||
|
|
||||||
resstr = self.client.serialize(resources_out)
|
|
||||||
|
|
||||||
path = getattr(self.client, cmd_resources + "_path")
|
|
||||||
if parent_id:
|
|
||||||
path = path % parent_id
|
|
||||||
self.client.httpclient.request(
|
|
||||||
end_url(path, format=self.format), 'GET',
|
|
||||||
body=None,
|
|
||||||
headers=mox.ContainsKeyValue(
|
|
||||||
'X-Auth-Token', TOKEN)).AndReturn((MyResp(200), resstr))
|
|
||||||
args = tuple(args) + ('--request-format', self.format)
|
|
||||||
self.mox.ReplayAll()
|
|
||||||
cmd_parser = cmd.get_parser("list_" + cmd_resources)
|
|
||||||
shell.run_command(cmd, cmd_parser, args)
|
|
||||||
self.mox.VerifyAll()
|
|
||||||
self.mox.UnsetStubs()
|
|
||||||
|
|
||||||
def _test_list_resources(self, resources, cmd, detail=False, tags=(),
|
|
||||||
fields_1=(), fields_2=(), page_size=None,
|
|
||||||
sort_key=(), sort_dir=(), response_contents=None,
|
|
||||||
base_args=None, path=None, cmd_resources=None,
|
|
||||||
parent_id=None):
|
|
||||||
self.mox.StubOutWithMock(cmd, "get_client")
|
|
||||||
self.mox.StubOutWithMock(self.client.httpclient, "request")
|
|
||||||
cmd.get_client().MultipleTimes().AndReturn(self.client)
|
|
||||||
if not cmd_resources:
|
|
||||||
cmd_resources = resources
|
|
||||||
if response_contents is None:
|
|
||||||
contents = [{self.id_field: 'myid1', },
|
|
||||||
{self.id_field: 'myid2', }, ]
|
|
||||||
else:
|
|
||||||
contents = response_contents
|
|
||||||
reses = {resources: contents}
|
|
||||||
self.client.format = self.format
|
|
||||||
resstr = self.client.serialize(reses)
|
|
||||||
# url method body
|
|
||||||
query = ""
|
|
||||||
args = base_args if base_args is not None else []
|
|
||||||
if detail:
|
|
||||||
args.append('-D')
|
|
||||||
args.extend(['--request-format', self.format])
|
|
||||||
if fields_1:
|
|
||||||
for field in fields_1:
|
|
||||||
args.append('--fields')
|
|
||||||
args.append(field)
|
|
||||||
|
|
||||||
if tags:
|
|
||||||
args.append('--')
|
|
||||||
args.append("--tag")
|
|
||||||
for tag in tags:
|
|
||||||
args.append(tag)
|
|
||||||
if isinstance(tag, unicode):
|
|
||||||
tag = urllib.quote(tag.encode('utf-8'))
|
|
||||||
if query:
|
|
||||||
query += "&tag=" + tag
|
|
||||||
else:
|
|
||||||
query = "tag=" + tag
|
|
||||||
if (not tags) and fields_2:
|
|
||||||
args.append('--')
|
|
||||||
if fields_2:
|
|
||||||
args.append("--fields")
|
|
||||||
for field in fields_2:
|
|
||||||
args.append(field)
|
|
||||||
if detail:
|
|
||||||
query = query and query + '&verbose=True' or 'verbose=True'
|
|
||||||
for field in itertools.chain(fields_1, fields_2):
|
|
||||||
if query:
|
|
||||||
query += "&fields=" + field
|
|
||||||
else:
|
|
||||||
query = "fields=" + field
|
|
||||||
if page_size:
|
|
||||||
args.append("--page-size")
|
|
||||||
args.append(str(page_size))
|
|
||||||
if query:
|
|
||||||
query += "&limit=%s" % page_size
|
|
||||||
else:
|
|
||||||
query = "limit=%s" % page_size
|
|
||||||
if sort_key:
|
|
||||||
for key in sort_key:
|
|
||||||
args.append('--sort-key')
|
|
||||||
args.append(key)
|
|
||||||
if query:
|
|
||||||
query += '&'
|
|
||||||
query += 'sort_key=%s' % key
|
|
||||||
if sort_dir:
|
|
||||||
len_diff = len(sort_key) - len(sort_dir)
|
|
||||||
if len_diff > 0:
|
|
||||||
sort_dir = tuple(sort_dir) + ('asc',) * len_diff
|
|
||||||
elif len_diff < 0:
|
|
||||||
sort_dir = sort_dir[:len(sort_key)]
|
|
||||||
for dir in sort_dir:
|
|
||||||
args.append('--sort-dir')
|
|
||||||
args.append(dir)
|
|
||||||
if query:
|
|
||||||
query += '&'
|
|
||||||
query += 'sort_dir=%s' % dir
|
|
||||||
if path is None:
|
|
||||||
path = getattr(self.client, cmd_resources + "_path")
|
|
||||||
if parent_id:
|
|
||||||
path = path % parent_id
|
|
||||||
self.client.httpclient.request(
|
|
||||||
MyUrlComparator(end_url(path, query, format=self.format),
|
|
||||||
self.client),
|
|
||||||
'GET',
|
|
||||||
body=None,
|
|
||||||
headers=mox.ContainsKeyValue(
|
|
||||||
'X-Auth-Token', TOKEN)).AndReturn((MyResp(200), resstr))
|
|
||||||
self.mox.ReplayAll()
|
|
||||||
cmd_parser = cmd.get_parser("list_" + cmd_resources)
|
|
||||||
shell.run_command(cmd, cmd_parser, args)
|
|
||||||
self.mox.VerifyAll()
|
|
||||||
self.mox.UnsetStubs()
|
|
||||||
_str = self.fake_stdout.make_string()
|
|
||||||
if response_contents is None:
|
|
||||||
self.assertIn('myid1', _str)
|
|
||||||
return _str
|
|
||||||
|
|
||||||
def _test_list_resources_with_pagination(self, resources, cmd,
|
|
||||||
cmd_resources=None,
|
|
||||||
parent_id=None):
|
|
||||||
self.mox.StubOutWithMock(cmd, "get_client")
|
|
||||||
self.mox.StubOutWithMock(self.client.httpclient, "request")
|
|
||||||
cmd.get_client().MultipleTimes().AndReturn(self.client)
|
|
||||||
if not cmd_resources:
|
|
||||||
cmd_resources = resources
|
|
||||||
|
|
||||||
path = getattr(self.client, cmd_resources + "_path")
|
|
||||||
if parent_id:
|
|
||||||
path = path % parent_id
|
|
||||||
fake_query = "marker=myid2&limit=2"
|
|
||||||
reses1 = {resources: [{'id': 'myid1', },
|
|
||||||
{'id': 'myid2', }],
|
|
||||||
'%s_links' % resources: [{'href': end_url(path, fake_query),
|
|
||||||
'rel': 'next'}]}
|
|
||||||
reses2 = {resources: [{'id': 'myid3', },
|
|
||||||
{'id': 'myid4', }]}
|
|
||||||
self.client.format = self.format
|
|
||||||
resstr1 = self.client.serialize(reses1)
|
|
||||||
resstr2 = self.client.serialize(reses2)
|
|
||||||
self.client.httpclient.request(
|
|
||||||
end_url(path, "", format=self.format), 'GET',
|
|
||||||
body=None,
|
|
||||||
headers=mox.ContainsKeyValue(
|
|
||||||
'X-Auth-Token', TOKEN)).AndReturn((MyResp(200), resstr1))
|
|
||||||
self.client.httpclient.request(
|
|
||||||
MyUrlComparator(end_url(path, fake_query, format=self.format),
|
|
||||||
self.client), 'GET',
|
|
||||||
body=None,
|
|
||||||
headers=mox.ContainsKeyValue(
|
|
||||||
'X-Auth-Token', TOKEN)).AndReturn((MyResp(200), resstr2))
|
|
||||||
self.mox.ReplayAll()
|
|
||||||
cmd_parser = cmd.get_parser("list_" + cmd_resources)
|
|
||||||
args = ['--request-format', self.format]
|
|
||||||
shell.run_command(cmd, cmd_parser, args)
|
|
||||||
self.mox.VerifyAll()
|
|
||||||
self.mox.UnsetStubs()
|
|
||||||
|
|
||||||
def _test_update_resource(self, resource, cmd, myid, args, extrafields,
|
|
||||||
cmd_resource=None, parent_id=None):
|
|
||||||
self.mox.StubOutWithMock(cmd, "get_client")
|
|
||||||
self.mox.StubOutWithMock(self.client.httpclient, "request")
|
|
||||||
cmd.get_client().MultipleTimes().AndReturn(self.client)
|
|
||||||
if not cmd_resource:
|
|
||||||
cmd_resource = resource
|
|
||||||
|
|
||||||
body = {resource: extrafields}
|
|
||||||
path = getattr(self.client, cmd_resource + "_path")
|
|
||||||
if parent_id:
|
|
||||||
path = path % (parent_id, myid)
|
|
||||||
else:
|
|
||||||
path = path % myid
|
|
||||||
self.client.format = self.format
|
|
||||||
# Work around for LP #1217791. XML deserializer called from
|
|
||||||
# MyComparator does not decodes XML string correctly.
|
|
||||||
if self.format == 'json':
|
|
||||||
mox_body = MyComparator(body, self.client)
|
|
||||||
else:
|
|
||||||
mox_body = self.client.serialize(body)
|
|
||||||
self.client.httpclient.request(
|
|
||||||
MyUrlComparator(end_url(path, format=self.format),
|
|
||||||
self.client),
|
|
||||||
'PUT',
|
|
||||||
body=mox_body,
|
|
||||||
headers=mox.ContainsKeyValue(
|
|
||||||
'X-Auth-Token', TOKEN)).AndReturn((MyResp(204), None))
|
|
||||||
args.extend(['--request-format', self.format])
|
|
||||||
self.mox.ReplayAll()
|
|
||||||
cmd_parser = cmd.get_parser("update_" + cmd_resource)
|
|
||||||
shell.run_command(cmd, cmd_parser, args)
|
|
||||||
self.mox.VerifyAll()
|
|
||||||
self.mox.UnsetStubs()
|
|
||||||
_str = self.fake_stdout.make_string()
|
|
||||||
self.assertIn(myid, _str)
|
|
||||||
|
|
||||||
def _test_show_resource(self, resource, cmd, myid, args, fields=(),
|
|
||||||
cmd_resource=None, parent_id=None):
|
|
||||||
self.mox.StubOutWithMock(cmd, "get_client")
|
|
||||||
self.mox.StubOutWithMock(self.client.httpclient, "request")
|
|
||||||
cmd.get_client().MultipleTimes().AndReturn(self.client)
|
|
||||||
if not cmd_resource:
|
|
||||||
cmd_resource = resource
|
|
||||||
|
|
||||||
query = "&".join(["fields=%s" % field for field in fields])
|
|
||||||
expected_res = {resource:
|
|
||||||
{self.id_field: myid,
|
|
||||||
'name': 'myname', }, }
|
|
||||||
self.client.format = self.format
|
|
||||||
resstr = self.client.serialize(expected_res)
|
|
||||||
path = getattr(self.client, cmd_resource + "_path")
|
|
||||||
if parent_id:
|
|
||||||
path = path % (parent_id, myid)
|
|
||||||
else:
|
|
||||||
path = path % myid
|
|
||||||
self.client.httpclient.request(
|
|
||||||
end_url(path, query, format=self.format), 'GET',
|
|
||||||
body=None,
|
|
||||||
headers=mox.ContainsKeyValue(
|
|
||||||
'X-Auth-Token', TOKEN)).AndReturn((MyResp(200), resstr))
|
|
||||||
args.extend(['--request-format', self.format])
|
|
||||||
self.mox.ReplayAll()
|
|
||||||
cmd_parser = cmd.get_parser("show_" + cmd_resource)
|
|
||||||
shell.run_command(cmd, cmd_parser, args)
|
|
||||||
self.mox.VerifyAll()
|
|
||||||
self.mox.UnsetStubs()
|
|
||||||
_str = self.fake_stdout.make_string()
|
|
||||||
self.assertIn(myid, _str)
|
|
||||||
self.assertIn('myname', _str)
|
|
||||||
|
|
||||||
def _test_delete_resource(self, resource, cmd, myid, args,
|
|
||||||
cmd_resource=None, parent_id=None):
|
|
||||||
self.mox.StubOutWithMock(cmd, "get_client")
|
|
||||||
self.mox.StubOutWithMock(self.client.httpclient, "request")
|
|
||||||
cmd.get_client().MultipleTimes().AndReturn(self.client)
|
|
||||||
if not cmd_resource:
|
|
||||||
cmd_resource = resource
|
|
||||||
path = getattr(self.client, cmd_resource + "_path")
|
|
||||||
if parent_id:
|
|
||||||
path = path % (parent_id, myid)
|
|
||||||
else:
|
|
||||||
path = path % (myid)
|
|
||||||
self.client.httpclient.request(
|
|
||||||
end_url(path, format=self.format), 'DELETE',
|
|
||||||
body=None,
|
|
||||||
headers=mox.ContainsKeyValue(
|
|
||||||
'X-Auth-Token', TOKEN)).AndReturn((MyResp(204), None))
|
|
||||||
args.extend(['--request-format', self.format])
|
|
||||||
self.mox.ReplayAll()
|
|
||||||
cmd_parser = cmd.get_parser("delete_" + cmd_resource)
|
|
||||||
shell.run_command(cmd, cmd_parser, args)
|
|
||||||
self.mox.VerifyAll()
|
|
||||||
self.mox.UnsetStubs()
|
|
||||||
_str = self.fake_stdout.make_string()
|
|
||||||
self.assertIn(myid, _str)
|
|
||||||
|
|
||||||
def _test_update_resource_action(self, resource, cmd, myid, action, args,
|
|
||||||
body, retval=None, cmd_resource=None):
|
|
||||||
self.mox.StubOutWithMock(cmd, "get_client")
|
|
||||||
self.mox.StubOutWithMock(self.client.httpclient, "request")
|
|
||||||
cmd.get_client().MultipleTimes().AndReturn(self.client)
|
|
||||||
if not cmd_resource:
|
|
||||||
cmd_resource = resource
|
|
||||||
path = getattr(self.client, cmd_resource + "_path")
|
|
||||||
path_action = '%s/%s' % (myid, action)
|
|
||||||
self.client.httpclient.request(
|
|
||||||
end_url(path % path_action, format=self.format), 'PUT',
|
|
||||||
body=MyComparator(body, self.client),
|
|
||||||
headers=mox.ContainsKeyValue(
|
|
||||||
'X-Auth-Token', TOKEN)).AndReturn((MyResp(204), retval))
|
|
||||||
args.extend(['--request-format', self.format])
|
|
||||||
self.mox.ReplayAll()
|
|
||||||
cmd_parser = cmd.get_parser("delete_" + cmd_resource)
|
|
||||||
shell.run_command(cmd, cmd_parser, args)
|
|
||||||
self.mox.VerifyAll()
|
|
||||||
self.mox.UnsetStubs()
|
|
||||||
_str = self.fake_stdout.make_string()
|
|
||||||
self.assertIn(myid, _str)
|
|
||||||
|
|
||||||
|
|
||||||
class ClientV2TestJson(CLITestV20Base):
|
class ClientV2TestJson(CLITestV20Base):
|
||||||
def test_do_request_unicode(self):
|
|
||||||
self.client.format = self.format
|
|
||||||
self.mox.StubOutWithMock(self.client.httpclient, "request")
|
|
||||||
unicode_text = u'\u7f51\u7edc'
|
|
||||||
# url with unicode
|
|
||||||
action = u'/test'
|
|
||||||
expected_action = action.encode('utf-8')
|
|
||||||
# query string with unicode
|
|
||||||
params = {'test': unicode_text}
|
|
||||||
expect_query = urllib.urlencode({'test':
|
|
||||||
unicode_text.encode('utf-8')})
|
|
||||||
# request body with unicode
|
|
||||||
body = params
|
|
||||||
expect_body = self.client.serialize(body)
|
|
||||||
# headers with unicode
|
|
||||||
self.client.httpclient.auth_token = unicode_text
|
|
||||||
expected_auth_token = unicode_text.encode('utf-8')
|
|
||||||
|
|
||||||
self.client.httpclient.request(
|
pass
|
||||||
end_url(expected_action, query=expect_query, format=self.format),
|
|
||||||
'PUT', body=expect_body,
|
|
||||||
headers=mox.ContainsKeyValue(
|
|
||||||
'X-Auth-Token',
|
|
||||||
expected_auth_token)).AndReturn((MyResp(200), expect_body))
|
|
||||||
|
|
||||||
self.mox.ReplayAll()
|
|
||||||
res_body = self.client.do_request('PUT', action, body=body,
|
|
||||||
params=params)
|
|
||||||
self.mox.VerifyAll()
|
|
||||||
self.mox.UnsetStubs()
|
|
||||||
|
|
||||||
# test response with unicode
|
|
||||||
self.assertEqual(res_body, body)
|
|
||||||
|
|
||||||
def test_do_request_error_without_response_body(self):
|
|
||||||
self.client.format = self.format
|
|
||||||
self.mox.StubOutWithMock(self.client.httpclient, "request")
|
|
||||||
params = {'test': 'value'}
|
|
||||||
expect_query = six.moves.urllib.parse.urlencode(params)
|
|
||||||
self.client.httpclient.auth_token = 'token'
|
|
||||||
|
|
||||||
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'), ''))
|
|
||||||
|
|
||||||
self.mox.ReplayAll()
|
|
||||||
error = self.assertRaises(exceptions.NeutronClientException,
|
|
||||||
self.client.do_request, 'PUT', '/test',
|
|
||||||
body='', params=params)
|
|
||||||
self.assertEqual("An error", str(error))
|
|
||||||
self.mox.VerifyAll()
|
|
||||||
self.mox.UnsetStubs()
|
|
||||||
|
|
||||||
|
|
||||||
class ClientV2UnicodeTestXML(ClientV2TestJson):
|
|
||||||
format = 'xml'
|
|
||||||
|
|
||||||
|
|
||||||
class CLITestV20ExceptionHandler(CLITestV20Base):
|
class CLITestV20ExceptionHandler(CLITestV20Base):
|
||||||
@ -634,7 +135,7 @@ class CLITestV20ExceptionHandler(CLITestV20Base):
|
|||||||
'detail': error_detail}}
|
'detail': error_detail}}
|
||||||
|
|
||||||
e = self.assertRaises(expected_exception,
|
e = self.assertRaises(expected_exception,
|
||||||
client.exception_handler_v20,
|
gbpclient.exception_handler_v20,
|
||||||
status_code, error_content)
|
status_code, error_content)
|
||||||
self.assertEqual(status_code, e.status_code)
|
self.assertEqual(status_code, e.status_code)
|
||||||
|
|
||||||
@ -645,44 +146,13 @@ class CLITestV20ExceptionHandler(CLITestV20Base):
|
|||||||
expected_msg = error_msg
|
expected_msg = error_msg
|
||||||
self.assertEqual(expected_msg, e.message)
|
self.assertEqual(expected_msg, e.message)
|
||||||
|
|
||||||
def test_exception_handler_v20_ip_address_in_use(self):
|
|
||||||
err_msg = ('Unable to complete operation for network '
|
|
||||||
'fake-network-uuid. The IP address fake-ip is in use.')
|
|
||||||
self._test_exception_handler_v20(
|
|
||||||
exceptions.IpAddressInUseClient, 409, err_msg,
|
|
||||||
'IpAddressInUse', err_msg, '')
|
|
||||||
|
|
||||||
def test_exception_handler_v20_neutron_known_error(self):
|
def test_exception_handler_v20_neutron_known_error(self):
|
||||||
known_error_map = [
|
# TODO(Sumit): This needs to be adapted for GBP
|
||||||
('NetworkNotFound', exceptions.NetworkNotFoundClient, 404),
|
pass
|
||||||
('PortNotFound', exceptions.PortNotFoundClient, 404),
|
|
||||||
('NetworkInUse', exceptions.NetworkInUseClient, 409),
|
|
||||||
('PortInUse', exceptions.PortInUseClient, 409),
|
|
||||||
('StateInvalid', exceptions.StateInvalidClient, 400),
|
|
||||||
('IpAddressInUse', exceptions.IpAddressInUseClient, 409),
|
|
||||||
('IpAddressGenerationFailure',
|
|
||||||
exceptions.IpAddressGenerationFailureClient, 409),
|
|
||||||
('MacAddressInUse', exceptions.MacAddressInUseClient, 409),
|
|
||||||
('ExternalIpAddressExhausted',
|
|
||||||
exceptions.ExternalIpAddressExhaustedClient, 400),
|
|
||||||
('OverQuota', exceptions.OverQuotaClient, 409),
|
|
||||||
]
|
|
||||||
|
|
||||||
error_msg = 'dummy exception message'
|
|
||||||
error_detail = 'sample detail'
|
|
||||||
for server_exc, client_exc, status_code in known_error_map:
|
|
||||||
self._test_exception_handler_v20(
|
|
||||||
client_exc, status_code,
|
|
||||||
error_msg + '\n' + error_detail,
|
|
||||||
server_exc, error_msg, error_detail)
|
|
||||||
|
|
||||||
def test_exception_handler_v20_neutron_known_error_without_detail(self):
|
def test_exception_handler_v20_neutron_known_error_without_detail(self):
|
||||||
error_msg = 'Network not found'
|
# TODO(Sumit): This needs to be adapted for GBP
|
||||||
error_detail = ''
|
pass
|
||||||
self._test_exception_handler_v20(
|
|
||||||
exceptions.NetworkNotFoundClient, 404,
|
|
||||||
error_msg,
|
|
||||||
'NetworkNotFound', error_msg, error_detail)
|
|
||||||
|
|
||||||
def test_exception_handler_v20_unknown_error_to_per_code_exception(self):
|
def test_exception_handler_v20_unknown_error_to_per_code_exception(self):
|
||||||
for status_code, client_exc in exceptions.HTTP_EXCEPTION_MAP.items():
|
for status_code, client_exc in exceptions.HTTP_EXCEPTION_MAP.items():
|
||||||
|
137
gbpclient/tests/unit/test_cli20_networkservicepolicy.py
Normal file
137
gbpclient/tests/unit/test_cli20_networkservicepolicy.py
Normal file
@ -0,0 +1,137 @@
|
|||||||
|
# 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 gbpclient.gbp.v2_0 import groupbasedpolicy as gbp
|
||||||
|
from gbpclient.tests.unit import test_cli20
|
||||||
|
|
||||||
|
|
||||||
|
class CLITestV20NetworkServicePolicyJSON(test_cli20.CLITestV20Base):
|
||||||
|
def setUp(self):
|
||||||
|
super(CLITestV20NetworkServicePolicyJSON, self).setUp()
|
||||||
|
|
||||||
|
def test_create_nsp_with_mandatory_params(self):
|
||||||
|
"""network-service-policy-create with mandatory params."""
|
||||||
|
resource = 'network_service_policy'
|
||||||
|
cmd = gbp.CreateNetworkServicePolicy(test_cli20.MyApp(sys.stdout),
|
||||||
|
None)
|
||||||
|
name = 'my-name'
|
||||||
|
tenant_id = 'my-tenant'
|
||||||
|
my_id = 'my-id'
|
||||||
|
args = ['--tenant-id', tenant_id,
|
||||||
|
name]
|
||||||
|
position_names = ['name', ]
|
||||||
|
position_values = [name, ]
|
||||||
|
self._test_create_resource(resource, cmd, name, my_id, args,
|
||||||
|
position_names, position_values,
|
||||||
|
tenant_id=tenant_id)
|
||||||
|
|
||||||
|
def test_create_network_service_policy_with_all_params(self):
|
||||||
|
"""network-service-policy-create with all params."""
|
||||||
|
resource = 'network_service_policy'
|
||||||
|
cmd = gbp.CreateNetworkServicePolicy(test_cli20.MyApp(sys.stdout),
|
||||||
|
None)
|
||||||
|
name = 'myname'
|
||||||
|
tenant_id = 'mytenant'
|
||||||
|
description = 'Mynsp'
|
||||||
|
my_id = 'someid'
|
||||||
|
network_svc_params = "type=ip_single,name=vip,value=self_subnet"
|
||||||
|
args = ['--tenant_id', tenant_id,
|
||||||
|
'--description', description,
|
||||||
|
'--network-service-params', network_svc_params,
|
||||||
|
name]
|
||||||
|
position_names = ['name', 'description', 'network_service_params']
|
||||||
|
net_params = [{"type": "ip_single", "name": "vip",
|
||||||
|
"value": "self_subnet"}]
|
||||||
|
position_values = [name, description, net_params]
|
||||||
|
self._test_create_resource(resource, cmd, name, my_id, args,
|
||||||
|
position_names, position_values,
|
||||||
|
tenant_id=tenant_id)
|
||||||
|
|
||||||
|
def test_list_network_service_policies(self):
|
||||||
|
"""network-sercvice-policy-list."""
|
||||||
|
resources = 'network_service_policies'
|
||||||
|
cmd = gbp.ListNetworkServicePolicy(test_cli20.MyApp(sys.stdout),
|
||||||
|
None)
|
||||||
|
self._test_list_resources(resources, cmd, True)
|
||||||
|
|
||||||
|
def test_list_network_service_policies_with_pagination(self):
|
||||||
|
"""network-sercvice-policy-list."""
|
||||||
|
resources = 'network_service_policies'
|
||||||
|
cmd = gbp.ListNetworkServicePolicy(test_cli20.MyApp(sys.stdout),
|
||||||
|
None)
|
||||||
|
self._test_list_resources_with_pagination(resources, cmd)
|
||||||
|
|
||||||
|
def test_list_network_sercice_policies_sort(self):
|
||||||
|
"""network-service-policy-list --sort-key name --sort-key id
|
||||||
|
--sort-key asc --sort-key desc
|
||||||
|
"""
|
||||||
|
resources = 'network_service_policies'
|
||||||
|
cmd = gbp.ListNetworkServicePolicy(test_cli20.MyApp(sys.stdout),
|
||||||
|
None)
|
||||||
|
self._test_list_resources(resources, cmd,
|
||||||
|
sort_key=["name", "id"],
|
||||||
|
sort_dir=["asc", "desc"])
|
||||||
|
|
||||||
|
def test_list_network_service_polices_limit(self):
|
||||||
|
"""network-service-policy-list -P."""
|
||||||
|
resources = 'network_service_policies'
|
||||||
|
cmd = gbp.ListNetworkServicePolicy(test_cli20.MyApp(sys.stdout),
|
||||||
|
None)
|
||||||
|
self._test_list_resources(resources, cmd, page_size=1000)
|
||||||
|
|
||||||
|
def test_show_network_service_policy_id(self):
|
||||||
|
"""network-service-policy-show test_id."""
|
||||||
|
resource = 'network_service_policy'
|
||||||
|
cmd = gbp.ShowNetworkServicePolicy(test_cli20.MyApp(sys.stdout), None)
|
||||||
|
args = ['--fields', 'id', self.test_id]
|
||||||
|
self._test_show_resource(resource, cmd, self.test_id, args, ['id'])
|
||||||
|
|
||||||
|
def test_show_network_service_policy_id_name(self):
|
||||||
|
"""network-service-policy-show."""
|
||||||
|
resource = 'network_service_policy'
|
||||||
|
cmd = gbp.ShowNetworkServicePolicy(test_cli20.MyApp(sys.stdout), None)
|
||||||
|
args = ['--fields', 'id', '--fields', 'name', self.test_id]
|
||||||
|
self._test_show_resource(resource, cmd, self.test_id,
|
||||||
|
args, ['id', 'name'])
|
||||||
|
|
||||||
|
def test_update_network_service_policy(self):
|
||||||
|
"""network-service-policy-update myid --name myname --tags a b."""
|
||||||
|
resource = 'network_service_policy'
|
||||||
|
cmd = gbp.UpdateNetworkServicePolicy(test_cli20.MyApp(sys.stdout),
|
||||||
|
None)
|
||||||
|
self._test_update_resource(resource, cmd, 'myid',
|
||||||
|
['myid', '--name', 'myname',
|
||||||
|
'--tags', 'a', 'b'],
|
||||||
|
{'name': 'myname', 'tags': ['a', 'b'], })
|
||||||
|
|
||||||
|
def test_update_network_service_policy_with_allparams(self):
|
||||||
|
resource = 'network_service_policy'
|
||||||
|
new_name = "new_name"
|
||||||
|
cmd = gbp.UpdateNetworkServicePolicy(test_cli20.MyApp(sys.stdout),
|
||||||
|
None)
|
||||||
|
body = {
|
||||||
|
'name': new_name
|
||||||
|
}
|
||||||
|
args = ['myid', '--name', new_name, '--request-format', 'json']
|
||||||
|
self._test_update_resource(resource, cmd, 'myid', args, body)
|
||||||
|
|
||||||
|
def test_delete_network_service_policy(self):
|
||||||
|
"""network-service-policy-delete my-id."""
|
||||||
|
resource = 'network_service_policy'
|
||||||
|
cmd = gbp.DeleteNetworkServicePolicy(test_cli20.MyApp(sys.stdout),
|
||||||
|
None)
|
||||||
|
my_id = 'my-id'
|
||||||
|
args = [my_id]
|
||||||
|
self._test_delete_resource(resource, cmd, my_id, args)
|
135
gbpclient/tests/unit/test_cli20_policyaction.py
Normal file
135
gbpclient/tests/unit/test_cli20_policyaction.py
Normal file
@ -0,0 +1,135 @@
|
|||||||
|
# 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 gbpclient.gbp.v2_0 import groupbasedpolicy as gbp
|
||||||
|
from gbpclient.tests.unit import test_cli20
|
||||||
|
|
||||||
|
|
||||||
|
class CLITestV20PolicyActionJSON(test_cli20.CLITestV20Base):
|
||||||
|
def setUp(self):
|
||||||
|
super(CLITestV20PolicyActionJSON, self).setUp()
|
||||||
|
|
||||||
|
def test_create_policy_action_with_mandatory_params(self):
|
||||||
|
"""grouppolicy-policy-action-create with all mandatory params."""
|
||||||
|
resource = 'policy_action'
|
||||||
|
cmd = gbp.CreatePolicyAction(test_cli20.MyApp(sys.stdout), None)
|
||||||
|
name = 'my-name'
|
||||||
|
tenant_id = 'my-tenant'
|
||||||
|
my_id = 'my-id'
|
||||||
|
args = ['--tenant-id', tenant_id,
|
||||||
|
name]
|
||||||
|
position_names = ['name', ]
|
||||||
|
position_values = [name, ]
|
||||||
|
self._test_create_resource(resource, cmd, name, my_id, args,
|
||||||
|
position_names, position_values,
|
||||||
|
tenant_id=tenant_id)
|
||||||
|
|
||||||
|
def test_create_policy_action_with_all_params(self):
|
||||||
|
"""grouppolicy-policy-action-create with all params."""
|
||||||
|
resource = 'policy_action'
|
||||||
|
cmd = gbp.CreatePolicyAction(test_cli20.MyApp(sys.stdout), None)
|
||||||
|
name = 'my-name'
|
||||||
|
tenant_id = 'my-tenant'
|
||||||
|
description = 'My PolicyAction'
|
||||||
|
my_id = 'my-id'
|
||||||
|
action_type = "allow"
|
||||||
|
action_value = "1234"
|
||||||
|
args = ['--tenant-id', tenant_id,
|
||||||
|
'--description', description,
|
||||||
|
'--action-type', action_type,
|
||||||
|
'--action-value', action_value,
|
||||||
|
name]
|
||||||
|
position_names = ['name', ]
|
||||||
|
position_values = [name, ]
|
||||||
|
self._test_create_resource(resource, cmd, name, my_id, args,
|
||||||
|
position_names, position_values,
|
||||||
|
tenant_id=tenant_id,
|
||||||
|
description=description,
|
||||||
|
action_type=action_type,
|
||||||
|
action_value=action_value)
|
||||||
|
|
||||||
|
def test_list_policy_actions(self):
|
||||||
|
"""grouppolicy-policy-action-list."""
|
||||||
|
resources = 'policy_actions'
|
||||||
|
cmd = gbp.ListPolicyAction(test_cli20.MyApp(sys.stdout), None)
|
||||||
|
self._test_list_resources(resources, cmd, True)
|
||||||
|
|
||||||
|
def test_list_policy_actions_pagination(self):
|
||||||
|
"""grouppolicy-policy-action-list."""
|
||||||
|
resources = 'policy_actions'
|
||||||
|
cmd = gbp.ListPolicyAction(test_cli20.MyApp(sys.stdout), None)
|
||||||
|
self._test_list_resources_with_pagination(resources, cmd)
|
||||||
|
|
||||||
|
def test_list_policy_actions_sort(self):
|
||||||
|
"""grouppolicy-policy-action-list --sort-key name --sort-key id
|
||||||
|
--sort-key asc --sort-key desc
|
||||||
|
"""
|
||||||
|
resources = 'policy_actions'
|
||||||
|
cmd = gbp.ListPolicyAction(test_cli20.MyApp(sys.stdout), None)
|
||||||
|
self._test_list_resources(resources, cmd,
|
||||||
|
sort_key=["name", "id"],
|
||||||
|
sort_dir=["asc", "desc"])
|
||||||
|
|
||||||
|
def test_list_policy_actions_limit(self):
|
||||||
|
"""grouppolicy-policy-action-list -P."""
|
||||||
|
resources = 'policy_actions'
|
||||||
|
cmd = gbp.ListPolicyAction(test_cli20.MyApp(sys.stdout), None)
|
||||||
|
self._test_list_resources(resources, cmd, page_size=1000)
|
||||||
|
|
||||||
|
def test_show_policy_action_id(self):
|
||||||
|
"""grouppolicy-policy-action-show test_id."""
|
||||||
|
resource = 'policy_action'
|
||||||
|
cmd = gbp.ShowPolicyAction(test_cli20.MyApp(sys.stdout), None)
|
||||||
|
args = ['--fields', 'id', self.test_id]
|
||||||
|
self._test_show_resource(resource, cmd, self.test_id, args, ['id'])
|
||||||
|
|
||||||
|
def test_show_policy_action_id_name(self):
|
||||||
|
"""grouppolicy-policy-action-show."""
|
||||||
|
resource = 'policy_action'
|
||||||
|
cmd = gbp.ShowPolicyAction(test_cli20.MyApp(sys.stdout), None)
|
||||||
|
args = ['--fields', 'id', '--fields', 'name', self.test_id]
|
||||||
|
self._test_show_resource(resource, cmd, self.test_id,
|
||||||
|
args, ['id', 'name'])
|
||||||
|
|
||||||
|
def test_update_policy_action(self):
|
||||||
|
"""grouppolicy-policy-action-update myid --name myname --tags a b."""
|
||||||
|
resource = 'policy_action'
|
||||||
|
cmd = gbp.UpdatePolicyAction(test_cli20.MyApp(sys.stdout), None)
|
||||||
|
self._test_update_resource(resource, cmd, 'myid',
|
||||||
|
['myid', '--name', 'myname',
|
||||||
|
'--tags', 'a', 'b'],
|
||||||
|
{'name': 'myname', 'tags': ['a', 'b'], })
|
||||||
|
|
||||||
|
def test_update_policy_action_with_allparams(self):
|
||||||
|
resource = 'policy_action'
|
||||||
|
action_type = "allow"
|
||||||
|
action_value = "1234"
|
||||||
|
cmd = gbp.UpdatePolicyAction(test_cli20.MyApp(sys.stdout), None)
|
||||||
|
body = {
|
||||||
|
'action_type': action_type,
|
||||||
|
'action_value': action_value
|
||||||
|
}
|
||||||
|
args = ['myid',
|
||||||
|
'--action-type', action_type,
|
||||||
|
'--action-value', action_value, ]
|
||||||
|
self._test_update_resource(resource, cmd, 'myid', args, body)
|
||||||
|
|
||||||
|
def test_delete_policy_action(self):
|
||||||
|
"""grouppolicy-policy-action-delete my-id."""
|
||||||
|
resource = 'policy_action'
|
||||||
|
cmd = gbp.DeletePolicyAction(test_cli20.MyApp(sys.stdout), None)
|
||||||
|
my_id = 'my-id'
|
||||||
|
args = [my_id]
|
||||||
|
self._test_delete_resource(resource, cmd, my_id, args)
|
141
gbpclient/tests/unit/test_cli20_policyclassifier.py
Normal file
141
gbpclient/tests/unit/test_cli20_policyclassifier.py
Normal file
@ -0,0 +1,141 @@
|
|||||||
|
# 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 gbpclient.gbp.v2_0 import groupbasedpolicy as gbp
|
||||||
|
from gbpclient.tests.unit import test_cli20
|
||||||
|
|
||||||
|
|
||||||
|
class CLITestV20PolicyClassifierJSON(test_cli20.CLITestV20Base):
|
||||||
|
def setUp(self):
|
||||||
|
super(CLITestV20PolicyClassifierJSON, self).setUp()
|
||||||
|
|
||||||
|
def test_create_policy_classifier_with_mandatory_params(self):
|
||||||
|
"""grouppolicy-policy-classifier-create with all mandatory params."""
|
||||||
|
resource = 'policy_classifier'
|
||||||
|
cmd = gbp.CreatePolicyClassifier(test_cli20.MyApp(sys.stdout), None)
|
||||||
|
name = 'my-name'
|
||||||
|
tenant_id = 'my-tenant'
|
||||||
|
my_id = 'my-id'
|
||||||
|
args = ['--tenant-id', tenant_id,
|
||||||
|
name]
|
||||||
|
position_names = ['name', ]
|
||||||
|
position_values = [name, ]
|
||||||
|
self._test_create_resource(resource, cmd, name, my_id, args,
|
||||||
|
position_names, position_values,
|
||||||
|
tenant_id=tenant_id)
|
||||||
|
|
||||||
|
def test_create_policy_classifier_with_all_params(self):
|
||||||
|
"""grouppolicy-policy-classifier-create with all params."""
|
||||||
|
resource = 'policy_classifier'
|
||||||
|
cmd = gbp.CreatePolicyClassifier(test_cli20.MyApp(sys.stdout), None)
|
||||||
|
name = 'my-name'
|
||||||
|
tenant_id = 'my-tenant'
|
||||||
|
description = 'My PolicyClassifier'
|
||||||
|
my_id = 'my-id'
|
||||||
|
protocol = 'tcp'
|
||||||
|
port_range = '10-80'
|
||||||
|
direction = 'in'
|
||||||
|
args = ['--tenant-id', tenant_id,
|
||||||
|
'--description', description,
|
||||||
|
'--protocol', protocol,
|
||||||
|
'--port-range', port_range,
|
||||||
|
'--direction', direction,
|
||||||
|
name]
|
||||||
|
position_names = ['name', ]
|
||||||
|
position_values = [name, ]
|
||||||
|
self._test_create_resource(resource, cmd, name, my_id, args,
|
||||||
|
position_names, position_values,
|
||||||
|
tenant_id=tenant_id,
|
||||||
|
description=description,
|
||||||
|
protocol=protocol,
|
||||||
|
port_range=port_range,
|
||||||
|
direction=direction)
|
||||||
|
|
||||||
|
def test_list_policy_classifiers(self):
|
||||||
|
"""grouppolicy-policy-classifier-list."""
|
||||||
|
resources = 'policy_classifiers'
|
||||||
|
cmd = gbp.ListPolicyClassifier(test_cli20.MyApp(sys.stdout), None)
|
||||||
|
self._test_list_resources(resources, cmd, True)
|
||||||
|
|
||||||
|
def test_list_policy_classifiers_pagination(self):
|
||||||
|
"""grouppolicy-policy-classifier-list."""
|
||||||
|
resources = 'policy_classifiers'
|
||||||
|
cmd = gbp.ListPolicyClassifier(test_cli20.MyApp(sys.stdout), None)
|
||||||
|
self._test_list_resources_with_pagination(resources, cmd)
|
||||||
|
|
||||||
|
def test_list_policy_classifiers_sort(self):
|
||||||
|
"""grouppolicy-policy-classifier-list --sort-key name --sort-key id
|
||||||
|
--sort-key asc --sort-key desc
|
||||||
|
"""
|
||||||
|
resources = 'policy_classifiers'
|
||||||
|
cmd = gbp.ListPolicyClassifier(test_cli20.MyApp(sys.stdout), None)
|
||||||
|
self._test_list_resources(resources, cmd,
|
||||||
|
sort_key=["name", "id"],
|
||||||
|
sort_dir=["asc", "desc"])
|
||||||
|
|
||||||
|
def test_list_policy_classifiers_limit(self):
|
||||||
|
"""grouppolicy-policy-classifier-list -P."""
|
||||||
|
resources = 'policy_classifiers'
|
||||||
|
cmd = gbp.ListPolicyClassifier(test_cli20.MyApp(sys.stdout), None)
|
||||||
|
self._test_list_resources(resources, cmd, page_size=1000)
|
||||||
|
|
||||||
|
def test_show_policy_classifier_id(self):
|
||||||
|
"""grouppolicy-policy-classifier-show test_id."""
|
||||||
|
resource = 'policy_classifier'
|
||||||
|
cmd = gbp.ShowPolicyClassifier(test_cli20.MyApp(sys.stdout), None)
|
||||||
|
args = ['--fields', 'id', self.test_id]
|
||||||
|
self._test_show_resource(resource, cmd, self.test_id, args, ['id'])
|
||||||
|
|
||||||
|
def test_show_policy_classifier_id_name(self):
|
||||||
|
"""grouppolicy-policy-classifier-show."""
|
||||||
|
resource = 'policy_classifier'
|
||||||
|
cmd = gbp.ShowPolicyClassifier(test_cli20.MyApp(sys.stdout), None)
|
||||||
|
args = ['--fields', 'id', '--fields', 'name', self.test_id]
|
||||||
|
self._test_show_resource(resource, cmd, self.test_id,
|
||||||
|
args, ['id', 'name'])
|
||||||
|
|
||||||
|
def test_update_policy_classifier(self):
|
||||||
|
"""grouppolicy-policy-classifier-update myid --name myname --tags a b.
|
||||||
|
"""
|
||||||
|
resource = 'policy_classifier'
|
||||||
|
cmd = gbp.UpdatePolicyClassifier(test_cli20.MyApp(sys.stdout), None)
|
||||||
|
self._test_update_resource(resource, cmd, 'myid',
|
||||||
|
['myid', '--name', 'myname',
|
||||||
|
'--tags', 'a', 'b'],
|
||||||
|
{'name': 'myname', 'tags': ['a', 'b'], })
|
||||||
|
|
||||||
|
def test_update_policy_classifier_with_allparams(self):
|
||||||
|
resource = 'policy_classifier'
|
||||||
|
protocol = 'tcp'
|
||||||
|
port_range = '10-80'
|
||||||
|
direction = 'in'
|
||||||
|
cmd = gbp.UpdatePolicyClassifier(test_cli20.MyApp(sys.stdout), None)
|
||||||
|
body = {
|
||||||
|
'protocol': protocol,
|
||||||
|
'port_range': port_range,
|
||||||
|
'direction': direction
|
||||||
|
}
|
||||||
|
args = ['myid', '--protocol', protocol,
|
||||||
|
'--port-range', port_range,
|
||||||
|
'--direction', direction, ]
|
||||||
|
self._test_update_resource(resource, cmd, 'myid', args, body)
|
||||||
|
|
||||||
|
def test_delete_policy_classifier(self):
|
||||||
|
"""grouppolicy-policy-classifier-delete my-id."""
|
||||||
|
resource = 'policy_classifier'
|
||||||
|
cmd = gbp.DeletePolicyClassifier(test_cli20.MyApp(sys.stdout), None)
|
||||||
|
my_id = 'my-id'
|
||||||
|
args = [my_id]
|
||||||
|
self._test_delete_resource(resource, cmd, my_id, args)
|
142
gbpclient/tests/unit/test_cli20_policyrule.py
Normal file
142
gbpclient/tests/unit/test_cli20_policyrule.py
Normal file
@ -0,0 +1,142 @@
|
|||||||
|
# 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 gbpclient.gbp.v2_0 import groupbasedpolicy as gbp
|
||||||
|
from gbpclient.tests.unit import test_cli20
|
||||||
|
|
||||||
|
|
||||||
|
class CLITestV20PolicyRuleJSON(test_cli20.CLITestV20Base):
|
||||||
|
def setUp(self):
|
||||||
|
super(CLITestV20PolicyRuleJSON, self).setUp()
|
||||||
|
|
||||||
|
def test_create_policy_rule_with_mandatory_params(self):
|
||||||
|
"""grouppolicy-policy-rule-create with all mandatory params."""
|
||||||
|
resource = 'policy_rule'
|
||||||
|
cmd = gbp.CreatePolicyRule(test_cli20.MyApp(sys.stdout), None)
|
||||||
|
name = 'my-name'
|
||||||
|
tenant_id = 'my-tenant'
|
||||||
|
my_id = 'my-id'
|
||||||
|
args = ['--tenant-id', tenant_id,
|
||||||
|
name]
|
||||||
|
position_names = ['name', ]
|
||||||
|
position_values = [name, ]
|
||||||
|
self._test_create_resource(resource, cmd, name, my_id, args,
|
||||||
|
position_names, position_values,
|
||||||
|
tenant_id=tenant_id)
|
||||||
|
|
||||||
|
def test_create_policy_rule_with_all_params(self):
|
||||||
|
"""grouppolicy-policy-rule-create with all params."""
|
||||||
|
resource = 'policy_rule'
|
||||||
|
cmd = gbp.CreatePolicyRule(test_cli20.MyApp(sys.stdout), None)
|
||||||
|
name = 'my-name'
|
||||||
|
tenant_id = 'my-tenant'
|
||||||
|
description = 'My PolicyRule'
|
||||||
|
my_id = 'my-id'
|
||||||
|
enabled = True
|
||||||
|
policy_classifier_id = 'pc-id'
|
||||||
|
policy_actions_res = ["pa1", "pa2"]
|
||||||
|
policy_actions_arg = "pa1 pa2"
|
||||||
|
args = ['--tenant-id', tenant_id,
|
||||||
|
'--description', description,
|
||||||
|
'--enabled', "True",
|
||||||
|
'--classifier', policy_classifier_id,
|
||||||
|
'--actions', policy_actions_arg,
|
||||||
|
name]
|
||||||
|
position_names = ['name', ]
|
||||||
|
position_values = [name, ]
|
||||||
|
self._test_create_resource(resource, cmd, name, my_id, args,
|
||||||
|
position_names, position_values,
|
||||||
|
tenant_id=tenant_id,
|
||||||
|
description=description,
|
||||||
|
enabled=enabled,
|
||||||
|
policy_classifier_id=policy_classifier_id,
|
||||||
|
policy_actions=policy_actions_res)
|
||||||
|
|
||||||
|
def test_list_policy_rules(self):
|
||||||
|
"""grouppolicy-policy-rule-list."""
|
||||||
|
resources = 'policy_rules'
|
||||||
|
cmd = gbp.ListPolicyRule(test_cli20.MyApp(sys.stdout), None)
|
||||||
|
self._test_list_resources(resources, cmd, True)
|
||||||
|
|
||||||
|
def test_list_policy_rules_pagination(self):
|
||||||
|
"""grouppolicy-policy-rule-list."""
|
||||||
|
resources = 'policy_rules'
|
||||||
|
cmd = gbp.ListPolicyRule(test_cli20.MyApp(sys.stdout), None)
|
||||||
|
self._test_list_resources_with_pagination(resources, cmd)
|
||||||
|
|
||||||
|
def test_list_policy_rules_sort(self):
|
||||||
|
"""grouppolicy-policy-rule-list --sort-key name --sort-key id
|
||||||
|
--sort-key asc --sort-key desc
|
||||||
|
"""
|
||||||
|
resources = 'policy_rules'
|
||||||
|
cmd = gbp.ListPolicyRule(test_cli20.MyApp(sys.stdout), None)
|
||||||
|
self._test_list_resources(resources, cmd,
|
||||||
|
sort_key=["name", "id"],
|
||||||
|
sort_dir=["asc", "desc"])
|
||||||
|
|
||||||
|
def test_list_policy_rules_limit(self):
|
||||||
|
"""grouppolicy-policy-rule-list -P."""
|
||||||
|
resources = 'policy_rules'
|
||||||
|
cmd = gbp.ListPolicyRule(test_cli20.MyApp(sys.stdout), None)
|
||||||
|
self._test_list_resources(resources, cmd, page_size=1000)
|
||||||
|
|
||||||
|
def test_show_policy_classifier_id(self):
|
||||||
|
"""grouppolicy-policy-rule-show test_id."""
|
||||||
|
resource = 'policy_rule'
|
||||||
|
cmd = gbp.ShowPolicyRule(test_cli20.MyApp(sys.stdout), None)
|
||||||
|
args = ['--fields', 'id', self.test_id]
|
||||||
|
self._test_show_resource(resource, cmd, self.test_id, args, ['id'])
|
||||||
|
|
||||||
|
def test_show_policy_classifier_id_name(self):
|
||||||
|
"""grouppolicy-policy-rule-show."""
|
||||||
|
resource = 'policy_rule'
|
||||||
|
cmd = gbp.ShowPolicyRule(test_cli20.MyApp(sys.stdout), None)
|
||||||
|
args = ['--fields', 'id', '--fields', 'name', self.test_id]
|
||||||
|
self._test_show_resource(resource, cmd, self.test_id,
|
||||||
|
args, ['id', 'name'])
|
||||||
|
|
||||||
|
def test_update_policy_rule(self):
|
||||||
|
"""grouppolicy-policy-rule-update myid --name myname --tags a b."""
|
||||||
|
resource = 'policy_rule'
|
||||||
|
cmd = gbp.UpdatePolicyRule(test_cli20.MyApp(sys.stdout), None)
|
||||||
|
self._test_update_resource(resource, cmd, 'myid',
|
||||||
|
['myid', '--name', 'myname',
|
||||||
|
'--tags', 'a', 'b'],
|
||||||
|
{'name': 'myname', 'tags': ['a', 'b'], })
|
||||||
|
|
||||||
|
def test_update_policy_rule_with_allparams(self):
|
||||||
|
resource = 'policy_rule'
|
||||||
|
enabled = True
|
||||||
|
policy_classifier_id = 'pc-id'
|
||||||
|
policy_actions_res = ["pa1", "pa2"]
|
||||||
|
policy_actions_arg = "pa1 pa2"
|
||||||
|
cmd = gbp.UpdatePolicyRule(test_cli20.MyApp(sys.stdout), None)
|
||||||
|
body = {
|
||||||
|
'policy_classifier_id': policy_classifier_id,
|
||||||
|
'enabled': enabled,
|
||||||
|
'policy_actions': policy_actions_res
|
||||||
|
}
|
||||||
|
args = ['myid', '--enabled', "True",
|
||||||
|
'--classifier', policy_classifier_id,
|
||||||
|
'--actions', policy_actions_arg, ]
|
||||||
|
self._test_update_resource(resource, cmd, 'myid', args, body)
|
||||||
|
|
||||||
|
def test_delete_policy_classifier(self):
|
||||||
|
"""grouppolicy-policy-rule-delete my-id."""
|
||||||
|
resource = 'policy_rule'
|
||||||
|
cmd = gbp.DeletePolicyRule(test_cli20.MyApp(sys.stdout), None)
|
||||||
|
my_id = 'my-id'
|
||||||
|
args = [my_id]
|
||||||
|
self._test_delete_resource(resource, cmd, my_id, args)
|
0
gbpclient/v2_0/__init__.py
Normal file
0
gbpclient/v2_0/__init__.py
Normal file
624
gbpclient/v2_0/client.py
Normal file
624
gbpclient/v2_0/client.py
Normal file
@ -0,0 +1,624 @@
|
|||||||
|
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||||
|
# not use this file except in compliance with the License. You may obtain
|
||||||
|
# a copy of the License at
|
||||||
|
#
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||||
|
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||||
|
# License for the specific language governing permissions and limitations
|
||||||
|
# under the License.
|
||||||
|
#
|
||||||
|
|
||||||
|
import logging
|
||||||
|
import time
|
||||||
|
import urllib
|
||||||
|
|
||||||
|
from neutronclient import client
|
||||||
|
from neutronclient.common import _
|
||||||
|
from neutronclient.common import constants
|
||||||
|
from neutronclient.common import exceptions
|
||||||
|
from neutronclient.common import serializer
|
||||||
|
from neutronclient.common import utils
|
||||||
|
import requests
|
||||||
|
import six.moves.urllib.parse as urlparse
|
||||||
|
|
||||||
|
|
||||||
|
_logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
def exception_handler_v20(status_code, error_content):
|
||||||
|
"""Exception handler for API v2.0 client
|
||||||
|
|
||||||
|
This routine generates the appropriate
|
||||||
|
Neutron exception according to the contents of the
|
||||||
|
response body
|
||||||
|
|
||||||
|
:param status_code: HTTP error status code
|
||||||
|
:param error_content: deserialized body of error response
|
||||||
|
"""
|
||||||
|
error_dict = None
|
||||||
|
if isinstance(error_content, dict):
|
||||||
|
error_dict = error_content.get('NeutronError')
|
||||||
|
# Find real error type
|
||||||
|
bad_neutron_error_flag = False
|
||||||
|
if error_dict:
|
||||||
|
# If Neutron key is found, it will definitely contain
|
||||||
|
# a 'message' and 'type' keys?
|
||||||
|
try:
|
||||||
|
error_type = error_dict['type']
|
||||||
|
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)
|
||||||
|
else:
|
||||||
|
message = None
|
||||||
|
if isinstance(error_content, dict):
|
||||||
|
message = error_content.get('message')
|
||||||
|
if message:
|
||||||
|
raise exceptions.NeutronClientException(status_code=status_code,
|
||||||
|
message=message)
|
||||||
|
|
||||||
|
# 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)
|
||||||
|
|
||||||
|
|
||||||
|
class APIParamsCall(object):
|
||||||
|
"""A Decorator to add support for format and tenant overriding
|
||||||
|
and filters
|
||||||
|
"""
|
||||||
|
def __init__(self, function):
|
||||||
|
self.function = function
|
||||||
|
|
||||||
|
def __get__(self, instance, owner):
|
||||||
|
def with_params(*args, **kwargs):
|
||||||
|
_format = instance.format
|
||||||
|
if 'format' in kwargs:
|
||||||
|
instance.format = kwargs['format']
|
||||||
|
ret = self.function(instance, *args, **kwargs)
|
||||||
|
instance.format = _format
|
||||||
|
return ret
|
||||||
|
return with_params
|
||||||
|
|
||||||
|
|
||||||
|
class Client(object):
|
||||||
|
"""Client for the GBP API.
|
||||||
|
|
||||||
|
:param string username: Username for authentication. (optional)
|
||||||
|
:param string user_id: User ID for authentication. (optional)
|
||||||
|
:param string password: Password for authentication. (optional)
|
||||||
|
:param string token: Token for authentication. (optional)
|
||||||
|
:param string tenant_name: Tenant name. (optional)
|
||||||
|
:param string tenant_id: Tenant id. (optional)
|
||||||
|
:param string auth_url: Keystone service endpoint for authorization.
|
||||||
|
:param string service_type: Network service type to pull from the
|
||||||
|
keystone catalog (e.g. 'network') (optional)
|
||||||
|
:param string endpoint_type: Network service endpoint type to pull from the
|
||||||
|
keystone catalog (e.g. 'publicURL',
|
||||||
|
'internalURL', or 'adminURL') (optional)
|
||||||
|
:param string region_name: Name of a region to select when choosing an
|
||||||
|
endpoint from the service catalog.
|
||||||
|
:param string endpoint_url: A user-supplied endpoint URL for the neutron
|
||||||
|
service. Lazy-authentication is possible for API
|
||||||
|
service calls if endpoint is set at
|
||||||
|
instantiation.(optional)
|
||||||
|
:param integer timeout: Allows customization of the timeout for client
|
||||||
|
http requests. (optional)
|
||||||
|
:param bool insecure: SSL certificate validation. (optional)
|
||||||
|
:param string ca_cert: SSL CA bundle file to use. (optional)
|
||||||
|
:param integer retries: How many times idempotent (GET, PUT, DELETE)
|
||||||
|
requests to Neutron server should be retried if
|
||||||
|
they fail (default: 0).
|
||||||
|
:param bool raise_errors: If True then exceptions caused by connection
|
||||||
|
failure are propagated to the caller.
|
||||||
|
(default: True)
|
||||||
|
:param session: Keystone client auth session to use. (optional)
|
||||||
|
:param auth: Keystone auth plugin to use. (optional)
|
||||||
|
|
||||||
|
Example::
|
||||||
|
|
||||||
|
from gbpclient.v2_0 import client
|
||||||
|
gbp = client.Client(username=USER,
|
||||||
|
password=PASS,
|
||||||
|
tenant_name=TENANT_NAME,
|
||||||
|
auth_url=KEYSTONE_URL)
|
||||||
|
|
||||||
|
ptgs = gbp.list_policy_target_groups()
|
||||||
|
...
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
endpoints_path = "/grouppolicy/endpoints"
|
||||||
|
endpoint_path = "/grouppolicy/endpoints/%s"
|
||||||
|
endpoint_groups_path = "/grouppolicy/endpoint_groups"
|
||||||
|
endpoint_group_path = "/grouppolicy/endpoint_groups/%s"
|
||||||
|
l2_policies_path = "/grouppolicy/l2_policies"
|
||||||
|
l2_policy_path = "/grouppolicy/l2_policies/%s"
|
||||||
|
l3_policies_path = "/grouppolicy/l3_policies"
|
||||||
|
l3_policy_path = "/grouppolicy/l3_policies/%s"
|
||||||
|
network_service_policies_path = "/grouppolicy/network_service_policies"
|
||||||
|
network_service_policy_path = "/grouppolicy/network_service_policies/%s"
|
||||||
|
policy_classifiers_path = "/grouppolicy/policy_classifiers"
|
||||||
|
policy_classifier_path = "/grouppolicy/policy_classifiers/%s"
|
||||||
|
policy_actions_path = "/grouppolicy/policy_actions"
|
||||||
|
policy_action_path = "/grouppolicy/policy_actions/%s"
|
||||||
|
policy_rules_path = "/grouppolicy/policy_rules"
|
||||||
|
policy_rule_path = "/grouppolicy/policy_rules/%s"
|
||||||
|
contracts_path = "/grouppolicy/contracts"
|
||||||
|
contract_path = "/grouppolicy/contracts/%s"
|
||||||
|
|
||||||
|
# API has no way to report plurals, so we have to hard code them
|
||||||
|
EXTED_PLURALS = {'endpoints': 'endpoint',
|
||||||
|
'endpoint_groups': 'endpoint_group',
|
||||||
|
'l2_policies': 'l2_policy',
|
||||||
|
'l3_policies': 'l3_policy',
|
||||||
|
'network_service_policies': 'network_service_policy',
|
||||||
|
'policy_classifiers': 'policy_classifier',
|
||||||
|
'policy_actions': 'policy_action',
|
||||||
|
'policy_rules': 'policy_rule',
|
||||||
|
'contracts': 'contract',
|
||||||
|
}
|
||||||
|
# 8192 Is the default max URI len for eventlet.wsgi.server
|
||||||
|
MAX_URI_LEN = 8192
|
||||||
|
|
||||||
|
def get_attr_metadata(self):
|
||||||
|
if self.format == 'json':
|
||||||
|
return {}
|
||||||
|
old_request_format = self.format
|
||||||
|
self.format = 'json'
|
||||||
|
exts = self.list_extensions()['extensions']
|
||||||
|
self.format = old_request_format
|
||||||
|
ns = dict([(ext['alias'], ext['namespace']) for ext in exts])
|
||||||
|
self.EXTED_PLURALS.update(constants.PLURALS)
|
||||||
|
return {'plurals': self.EXTED_PLURALS,
|
||||||
|
'xmlns': constants.XML_NS_V20,
|
||||||
|
constants.EXT_NS: ns}
|
||||||
|
|
||||||
|
@APIParamsCall
|
||||||
|
def list_extensions(self, **_params):
|
||||||
|
"""Fetch a list of all exts on server side."""
|
||||||
|
return self.get(self.extensions_path, params=_params)
|
||||||
|
|
||||||
|
@APIParamsCall
|
||||||
|
def show_extension(self, ext_alias, **_params):
|
||||||
|
"""Fetch a list of all exts on server side."""
|
||||||
|
return self.get(self.extension_path % ext_alias, params=_params)
|
||||||
|
|
||||||
|
@APIParamsCall
|
||||||
|
def list_endpoints(self, retrieve_all=True, **_params):
|
||||||
|
"""Fetches a list of all endpoints for a tenant."""
|
||||||
|
# Pass filters in "params" argument to do_request
|
||||||
|
return self.list('endpoints', self.endpoints_path, retrieve_all,
|
||||||
|
**_params)
|
||||||
|
|
||||||
|
@APIParamsCall
|
||||||
|
def show_endpoint(self, endpoint, **_params):
|
||||||
|
"""Fetches information of a certain endpoint."""
|
||||||
|
return self.get(self.endpoint_path % (endpoint), params=_params)
|
||||||
|
|
||||||
|
@APIParamsCall
|
||||||
|
def create_endpoint(self, body=None):
|
||||||
|
"""Creates a new endpoint."""
|
||||||
|
return self.post(self.endpoints_path, body=body)
|
||||||
|
|
||||||
|
@APIParamsCall
|
||||||
|
def update_endpoint(self, endpoint, body=None):
|
||||||
|
"""Updates a endpoint."""
|
||||||
|
return self.put(self.endpoint_path % (endpoint), body=body)
|
||||||
|
|
||||||
|
@APIParamsCall
|
||||||
|
def delete_endpoint(self, endpoint):
|
||||||
|
"""Deletes the specified endpoint."""
|
||||||
|
return self.delete(self.endpoint_path % (endpoint))
|
||||||
|
|
||||||
|
@APIParamsCall
|
||||||
|
def list_endpoint_groups(self, retrieve_all=True, **_params):
|
||||||
|
"""Fetches a list of all endpoint_groups for a tenant."""
|
||||||
|
# Pass filters in "params" argument to do_request
|
||||||
|
return self.list('endpoint_groups', self.endpoint_groups_path,
|
||||||
|
retrieve_all, **_params)
|
||||||
|
|
||||||
|
@APIParamsCall
|
||||||
|
def show_endpoint_group(self, endpoint_group, **_params):
|
||||||
|
"""Fetches information of a certain endpoint_group."""
|
||||||
|
return self.get(self.endpoint_group_path % (endpoint_group),
|
||||||
|
params=_params)
|
||||||
|
|
||||||
|
@APIParamsCall
|
||||||
|
def create_endpoint_group(self, body=None):
|
||||||
|
"""Creates a new endpoint_group."""
|
||||||
|
return self.post(self.endpoint_groups_path, body=body)
|
||||||
|
|
||||||
|
@APIParamsCall
|
||||||
|
def update_endpoint_group(self, endpoint_group, body=None):
|
||||||
|
"""Updates a endpoint_group."""
|
||||||
|
return self.put(self.endpoint_group_path % (endpoint_group),
|
||||||
|
body=body)
|
||||||
|
|
||||||
|
@APIParamsCall
|
||||||
|
def delete_endpoint_group(self, endpoint_group):
|
||||||
|
"""Deletes the specified endpoint_group."""
|
||||||
|
return self.delete(self.endpoint_group_path % (endpoint_group))
|
||||||
|
|
||||||
|
@APIParamsCall
|
||||||
|
def list_l2_policies(self, retrieve_all=True, **_params):
|
||||||
|
"""Fetches a list of all l2_policies for a tenant."""
|
||||||
|
# Pass filters in "params" argument to do_request
|
||||||
|
return self.list('l2_policies', self.l2_policies_path,
|
||||||
|
retrieve_all, **_params)
|
||||||
|
|
||||||
|
@APIParamsCall
|
||||||
|
def show_l2_policy(self, l2_policy, **_params):
|
||||||
|
"""Fetches information of a certain l2_policy."""
|
||||||
|
return self.get(self.l2_policy_path % (l2_policy),
|
||||||
|
params=_params)
|
||||||
|
|
||||||
|
@APIParamsCall
|
||||||
|
def create_l2_policy(self, body=None):
|
||||||
|
"""Creates a new l2_policy."""
|
||||||
|
return self.post(self.l2_policies_path, body=body)
|
||||||
|
|
||||||
|
@APIParamsCall
|
||||||
|
def update_l2_policy(self, l2_policy, body=None):
|
||||||
|
"""Updates a l2_policy."""
|
||||||
|
return self.put(self.l2_policy_path % (l2_policy), body=body)
|
||||||
|
|
||||||
|
@APIParamsCall
|
||||||
|
def delete_l2_policy(self, l2_policy):
|
||||||
|
"""Deletes the specified l2_policy."""
|
||||||
|
return self.delete(self.l2_policy_path % (l2_policy))
|
||||||
|
|
||||||
|
@APIParamsCall
|
||||||
|
def list_network_service_policies(self, retrieve_all=True, **_params):
|
||||||
|
"""Fetches a list of all network_service_policies for a tenant."""
|
||||||
|
# Pass filters in "params" argument to do_request
|
||||||
|
return self.list('network_service_policies',
|
||||||
|
self.network_service_policies_path,
|
||||||
|
retrieve_all, **_params)
|
||||||
|
|
||||||
|
@APIParamsCall
|
||||||
|
def show_network_service_policy(self, network_service_policy, **_params):
|
||||||
|
"""Fetches information of a certain network_service_policy."""
|
||||||
|
return self.get(
|
||||||
|
self.network_service_policy_path % (network_service_policy),
|
||||||
|
params=_params)
|
||||||
|
|
||||||
|
@APIParamsCall
|
||||||
|
def create_network_service_policy(self, body=None):
|
||||||
|
"""Creates a new network_service_policy."""
|
||||||
|
return self.post(self.network_service_policies_path, body=body)
|
||||||
|
|
||||||
|
@APIParamsCall
|
||||||
|
def update_network_service_policy(self, network_service_policy, body=None):
|
||||||
|
"""Updates a network_service_policy."""
|
||||||
|
return self.put(
|
||||||
|
self.network_service_policy_path % (network_service_policy),
|
||||||
|
body=body)
|
||||||
|
|
||||||
|
@APIParamsCall
|
||||||
|
def delete_network_service_policy(self, network_service_policy):
|
||||||
|
"""Deletes the specified network_service_policy."""
|
||||||
|
return self.delete(
|
||||||
|
self.network_service_policy_path % (network_service_policy))
|
||||||
|
|
||||||
|
@APIParamsCall
|
||||||
|
def list_l3_policies(self, retrieve_all=True, **_params):
|
||||||
|
"""Fetches a list of all l3_policies for a tenant."""
|
||||||
|
# Pass filters in "params" argument to do_request
|
||||||
|
return self.list('l3_policies', self.l3_policies_path,
|
||||||
|
retrieve_all, **_params)
|
||||||
|
|
||||||
|
@APIParamsCall
|
||||||
|
def show_l3_policy(self, l3_policy, **_params):
|
||||||
|
"""Fetches information of a certain l3_policy."""
|
||||||
|
return self.get(self.l3_policy_path % (l3_policy),
|
||||||
|
params=_params)
|
||||||
|
|
||||||
|
@APIParamsCall
|
||||||
|
def create_l3_policy(self, body=None):
|
||||||
|
"""Creates a new l3_policy."""
|
||||||
|
return self.post(self.l3_policies_path, body=body)
|
||||||
|
|
||||||
|
@APIParamsCall
|
||||||
|
def update_l3_policy(self, l3_policy, body=None):
|
||||||
|
"""Updates a l3_policy."""
|
||||||
|
return self.put(self.l3_policy_path % (l3_policy),
|
||||||
|
body=body)
|
||||||
|
|
||||||
|
@APIParamsCall
|
||||||
|
def delete_l3_policy(self, l3_policy):
|
||||||
|
"""Deletes the specified l3_policy."""
|
||||||
|
return self.delete(self.l3_policy_path % (l3_policy))
|
||||||
|
|
||||||
|
@APIParamsCall
|
||||||
|
def list_policy_classifiers(self, retrieve_all=True, **_params):
|
||||||
|
"""Fetches a list of all policy_classifiers for a tenant."""
|
||||||
|
# Pass filters in "params" argument to do_request
|
||||||
|
return self.list('policy_classifiers', self.policy_classifiers_path,
|
||||||
|
retrieve_all, **_params)
|
||||||
|
|
||||||
|
@APIParamsCall
|
||||||
|
def show_policy_classifier(self, policy_classifier, **_params):
|
||||||
|
"""Fetches information of a certain policy_classifier."""
|
||||||
|
return self.get(self.policy_classifier_path % (policy_classifier),
|
||||||
|
params=_params)
|
||||||
|
|
||||||
|
@APIParamsCall
|
||||||
|
def create_policy_classifier(self, body=None):
|
||||||
|
"""Creates a new policy_classifier."""
|
||||||
|
return self.post(self.policy_classifiers_path, body=body)
|
||||||
|
|
||||||
|
@APIParamsCall
|
||||||
|
def update_policy_classifier(self, policy_classifier, body=None):
|
||||||
|
"""Updates a policy_classifier."""
|
||||||
|
return self.put(self.policy_classifier_path % (policy_classifier),
|
||||||
|
body=body)
|
||||||
|
|
||||||
|
@APIParamsCall
|
||||||
|
def delete_policy_classifier(self, policy_classifier):
|
||||||
|
"""Deletes the specified policy_classifier."""
|
||||||
|
return self.delete(self.policy_classifier_path % (policy_classifier))
|
||||||
|
|
||||||
|
@APIParamsCall
|
||||||
|
def list_policy_actions(self, retrieve_all=True, **_params):
|
||||||
|
"""Fetches a list of all policy_actions for a tenant."""
|
||||||
|
# Pass filters in "params" argument to do_request
|
||||||
|
return self.list('policy_actions', self.policy_actions_path,
|
||||||
|
retrieve_all, **_params)
|
||||||
|
|
||||||
|
@APIParamsCall
|
||||||
|
def show_policy_action(self, policy_action, **_params):
|
||||||
|
"""Fetches information of a certain policy_action."""
|
||||||
|
return self.get(self.policy_action_path % (policy_action),
|
||||||
|
params=_params)
|
||||||
|
|
||||||
|
@APIParamsCall
|
||||||
|
def create_policy_action(self, body=None):
|
||||||
|
"""Creates a new policy_action."""
|
||||||
|
return self.post(self.policy_actions_path, body=body)
|
||||||
|
|
||||||
|
@APIParamsCall
|
||||||
|
def update_policy_action(self, policy_action, body=None):
|
||||||
|
"""Updates a policy_action."""
|
||||||
|
return self.put(self.policy_action_path % (policy_action), body=body)
|
||||||
|
|
||||||
|
@APIParamsCall
|
||||||
|
def delete_policy_action(self, policy_action):
|
||||||
|
"""Deletes the specified policy_action."""
|
||||||
|
return self.delete(self.policy_action_path % (policy_action))
|
||||||
|
|
||||||
|
@APIParamsCall
|
||||||
|
def list_policy_rules(self, retrieve_all=True, **_params):
|
||||||
|
"""Fetches a list of all policy_rules for a tenant."""
|
||||||
|
# Pass filters in "params" argument to do_request
|
||||||
|
return self.list('policy_rules', self.policy_rules_path, retrieve_all,
|
||||||
|
**_params)
|
||||||
|
|
||||||
|
@APIParamsCall
|
||||||
|
def show_policy_rule(self, policy_rule, **_params):
|
||||||
|
"""Fetches information of a certain policy_rule."""
|
||||||
|
return self.get(self.policy_rule_path % (policy_rule), params=_params)
|
||||||
|
|
||||||
|
@APIParamsCall
|
||||||
|
def create_policy_rule(self, body=None):
|
||||||
|
"""Creates a new policy_rule."""
|
||||||
|
return self.post(self.policy_rules_path, body=body)
|
||||||
|
|
||||||
|
@APIParamsCall
|
||||||
|
def update_policy_rule(self, policy_rule, body=None):
|
||||||
|
"""Updates a policy_rule."""
|
||||||
|
return self.put(self.policy_rule_path % (policy_rule), body=body)
|
||||||
|
|
||||||
|
@APIParamsCall
|
||||||
|
def delete_policy_rule(self, policy_rule):
|
||||||
|
"""Deletes the specified policy_rule."""
|
||||||
|
return self.delete(self.policy_rule_path % (policy_rule))
|
||||||
|
|
||||||
|
@APIParamsCall
|
||||||
|
def list_contracts(self, retrieve_all=True, **_params):
|
||||||
|
"""Fetches a list of all contracts for a tenant."""
|
||||||
|
# Pass filters in "params" argument to do_request
|
||||||
|
return self.list('contracts', self.contracts_path, retrieve_all,
|
||||||
|
**_params)
|
||||||
|
|
||||||
|
@APIParamsCall
|
||||||
|
def show_contract(self, contract, **_params):
|
||||||
|
"""Fetches information of a certain contract."""
|
||||||
|
return self.get(self.contract_path % (contract), params=_params)
|
||||||
|
|
||||||
|
@APIParamsCall
|
||||||
|
def create_contract(self, body=None):
|
||||||
|
"""Creates a new contract."""
|
||||||
|
return self.post(self.contracts_path, body=body)
|
||||||
|
|
||||||
|
@APIParamsCall
|
||||||
|
def update_contract(self, contract, body=None):
|
||||||
|
"""Updates a contract."""
|
||||||
|
return self.put(self.contract_path % (contract), body=body)
|
||||||
|
|
||||||
|
@APIParamsCall
|
||||||
|
def delete_contract(self, contract):
|
||||||
|
"""Deletes the specified contract."""
|
||||||
|
return self.delete(self.contract_path % (contract))
|
||||||
|
|
||||||
|
def __init__(self, **kwargs):
|
||||||
|
"""Initialize a new client for the GBP v2.0 API."""
|
||||||
|
super(Client, self).__init__()
|
||||||
|
self.retries = kwargs.pop('retries', 0)
|
||||||
|
self.raise_errors = kwargs.pop('raise_errors', True)
|
||||||
|
self.httpclient = client.construct_http_client(**kwargs)
|
||||||
|
self.version = '2.0'
|
||||||
|
self.format = 'json'
|
||||||
|
self.action_prefix = "/v%s" % (self.version)
|
||||||
|
self.retry_interval = 1
|
||||||
|
|
||||||
|
def _handle_fault_response(self, status_code, response_body):
|
||||||
|
# Create exception with HTTP status code and message
|
||||||
|
_logger.debug("Error message: %s", response_body)
|
||||||
|
# Add deserialized error message to exception arguments
|
||||||
|
try:
|
||||||
|
des_error_body = self.deserialize(response_body, status_code)
|
||||||
|
except Exception:
|
||||||
|
# If unable to deserialized body it is probably not a
|
||||||
|
# Neutron error
|
||||||
|
des_error_body = {'message': response_body}
|
||||||
|
# Raise the appropriate exception
|
||||||
|
exception_handler_v20(status_code, des_error_body)
|
||||||
|
|
||||||
|
def _check_uri_length(self, action):
|
||||||
|
uri_len = len(self.httpclient.endpoint_url) + len(action)
|
||||||
|
if uri_len > self.MAX_URI_LEN:
|
||||||
|
raise exceptions.RequestURITooLong(
|
||||||
|
excess=uri_len - self.MAX_URI_LEN)
|
||||||
|
|
||||||
|
def do_request(self, method, action, body=None, headers=None, params=None):
|
||||||
|
# Add format and tenant_id
|
||||||
|
action += ".%s" % self.format
|
||||||
|
action = self.action_prefix + action
|
||||||
|
if type(params) is dict and params:
|
||||||
|
params = utils.safe_encode_dict(params)
|
||||||
|
action += '?' + urllib.urlencode(params, doseq=1)
|
||||||
|
# Ensure client always has correct uri - do not guesstimate anything
|
||||||
|
self.httpclient.authenticate_and_fetch_endpoint_url()
|
||||||
|
self._check_uri_length(action)
|
||||||
|
|
||||||
|
if body:
|
||||||
|
body = self.serialize(body)
|
||||||
|
self.httpclient.content_type = self.content_type()
|
||||||
|
resp, replybody = self.httpclient.do_request(action, method, body=body)
|
||||||
|
status_code = resp.status_code
|
||||||
|
if status_code in (requests.codes.ok,
|
||||||
|
requests.codes.created,
|
||||||
|
requests.codes.accepted,
|
||||||
|
requests.codes.no_content):
|
||||||
|
return self.deserialize(replybody, status_code)
|
||||||
|
else:
|
||||||
|
if not replybody:
|
||||||
|
replybody = resp.reason
|
||||||
|
self._handle_fault_response(status_code, replybody)
|
||||||
|
|
||||||
|
def get_auth_info(self):
|
||||||
|
return self.httpclient.get_auth_info()
|
||||||
|
|
||||||
|
def serialize(self, data):
|
||||||
|
"""Serializes a dictionary into either XML or JSON.
|
||||||
|
|
||||||
|
A dictionary with a single key can be passed and
|
||||||
|
it can contain any structure.
|
||||||
|
"""
|
||||||
|
if data is None:
|
||||||
|
return None
|
||||||
|
elif type(data) is dict:
|
||||||
|
return serializer.Serializer(
|
||||||
|
self.get_attr_metadata()).serialize(data, self.content_type())
|
||||||
|
else:
|
||||||
|
raise Exception(_("Unable to serialize object of type = '%s'") %
|
||||||
|
type(data))
|
||||||
|
|
||||||
|
def deserialize(self, data, status_code):
|
||||||
|
"""Deserializes an XML or JSON string into a dictionary."""
|
||||||
|
if status_code == 204:
|
||||||
|
return data
|
||||||
|
return serializer.Serializer(self.get_attr_metadata()).deserialize(
|
||||||
|
data, self.content_type())['body']
|
||||||
|
|
||||||
|
def content_type(self, _format=None):
|
||||||
|
"""Returns the mime-type for either 'xml' or 'json'.
|
||||||
|
|
||||||
|
Defaults to the currently set format.
|
||||||
|
"""
|
||||||
|
_format = _format or self.format
|
||||||
|
return "application/%s" % (_format)
|
||||||
|
|
||||||
|
def retry_request(self, method, action, body=None,
|
||||||
|
headers=None, params=None):
|
||||||
|
"""Call do_request with the default retry configuration.
|
||||||
|
|
||||||
|
Only idempotent requests should retry failed connection attempts.
|
||||||
|
:raises: ConnectionFailed if the maximum # of retries is exceeded
|
||||||
|
"""
|
||||||
|
max_attempts = self.retries + 1
|
||||||
|
for i in range(max_attempts):
|
||||||
|
try:
|
||||||
|
return self.do_request(method, action, body=body,
|
||||||
|
headers=headers, params=params)
|
||||||
|
except exceptions.ConnectionFailed:
|
||||||
|
# Exception has already been logged by do_request()
|
||||||
|
if i < self.retries:
|
||||||
|
_logger.debug('Retrying connection to Neutron service')
|
||||||
|
time.sleep(self.retry_interval)
|
||||||
|
elif self.raise_errors:
|
||||||
|
raise
|
||||||
|
|
||||||
|
if self.retries:
|
||||||
|
msg = (_("Failed to connect to Neutron server after %d attempts")
|
||||||
|
% max_attempts)
|
||||||
|
else:
|
||||||
|
msg = _("Failed to connect Neutron server")
|
||||||
|
|
||||||
|
raise exceptions.ConnectionFailed(reason=msg)
|
||||||
|
|
||||||
|
def delete(self, action, body=None, headers=None, params=None):
|
||||||
|
return self.retry_request("DELETE", action, body=body,
|
||||||
|
headers=headers, params=params)
|
||||||
|
|
||||||
|
def get(self, action, body=None, headers=None, params=None):
|
||||||
|
return self.retry_request("GET", action, body=body,
|
||||||
|
headers=headers, params=params)
|
||||||
|
|
||||||
|
def post(self, action, body=None, headers=None, params=None):
|
||||||
|
# Do not retry POST requests to avoid the orphan objects problem.
|
||||||
|
return self.do_request("POST", action, body=body,
|
||||||
|
headers=headers, params=params)
|
||||||
|
|
||||||
|
def put(self, action, body=None, headers=None, params=None):
|
||||||
|
return self.retry_request("PUT", action, body=body,
|
||||||
|
headers=headers, params=params)
|
||||||
|
|
||||||
|
def list(self, collection, path, retrieve_all=True, **params):
|
||||||
|
if retrieve_all:
|
||||||
|
res = []
|
||||||
|
for r in self._pagination(collection, path, **params):
|
||||||
|
res.extend(r[collection])
|
||||||
|
return {collection: res}
|
||||||
|
else:
|
||||||
|
return self._pagination(collection, path, **params)
|
||||||
|
|
||||||
|
def _pagination(self, collection, path, **params):
|
||||||
|
if params.get('page_reverse', False):
|
||||||
|
linkrel = 'previous'
|
||||||
|
else:
|
||||||
|
linkrel = 'next'
|
||||||
|
next = True
|
||||||
|
while next:
|
||||||
|
res = self.get(path, params=params)
|
||||||
|
yield res
|
||||||
|
next = False
|
||||||
|
try:
|
||||||
|
for link in res['%s_links' % collection]:
|
||||||
|
if link['rel'] == linkrel:
|
||||||
|
query_str = urlparse.urlparse(link['href']).query
|
||||||
|
params = urlparse.parse_qs(query_str)
|
||||||
|
next = True
|
||||||
|
break
|
||||||
|
except KeyError:
|
||||||
|
break
|
227
run_tests.sh
Executable file
227
run_tests.sh
Executable file
@ -0,0 +1,227 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
set -eu
|
||||||
|
|
||||||
|
function usage {
|
||||||
|
echo "Usage: $0 [OPTION]..."
|
||||||
|
echo "Run Group Based Policy's test suite(s)"
|
||||||
|
echo ""
|
||||||
|
echo " -V, --virtual-env Always use virtualenv. Install automatically if not present"
|
||||||
|
echo " -N, --no-virtual-env Don't use virtualenv. Run tests in local environment"
|
||||||
|
echo " -s, --no-site-packages Isolate the virtualenv from the global Python environment"
|
||||||
|
echo " -r, --recreate-db Recreate the test database (deprecated, as this is now the default)."
|
||||||
|
echo " -n, --no-recreate-db Don't recreate the test database."
|
||||||
|
echo " -f, --force Force a clean re-build of the virtual environment. Useful when dependencies have been added."
|
||||||
|
echo " -u, --update Update the virtual environment with any newer package versions"
|
||||||
|
echo " -p, --pep8 Just run PEP8 and HACKING compliance check"
|
||||||
|
echo " -P, --no-pep8 Don't run static code checks"
|
||||||
|
echo " -c, --coverage Generate coverage report"
|
||||||
|
echo " -d, --debug Run tests with testtools instead of testr. This allows you to use the debugger."
|
||||||
|
echo " -h, --help Print this usage message"
|
||||||
|
echo " --virtual-env-path <path> Location of the virtualenv directory"
|
||||||
|
echo " Default: \$(pwd)"
|
||||||
|
echo " --virtual-env-name <name> Name of the virtualenv directory"
|
||||||
|
echo " Default: .venv"
|
||||||
|
echo " --tools-path <dir> Location of the tools directory"
|
||||||
|
echo " Default: \$(pwd)"
|
||||||
|
echo ""
|
||||||
|
echo "Note: with no options specified, the script will try to run the tests in a virtual environment,"
|
||||||
|
echo " If no virtualenv is found, the script will ask if you would like to create one. If you "
|
||||||
|
echo " prefer to run tests NOT in a virtual environment, simply pass the -N option."
|
||||||
|
exit
|
||||||
|
}
|
||||||
|
|
||||||
|
function process_options {
|
||||||
|
i=1
|
||||||
|
while [ $i -le $# ]; do
|
||||||
|
case "${!i}" in
|
||||||
|
-h|--help) usage;;
|
||||||
|
-V|--virtual-env) always_venv=1; never_venv=0;;
|
||||||
|
-N|--no-virtual-env) always_venv=0; never_venv=1;;
|
||||||
|
-s|--no-site-packages) no_site_packages=1;;
|
||||||
|
-r|--recreate-db) recreate_db=1;;
|
||||||
|
-n|--no-recreate-db) recreate_db=0;;
|
||||||
|
-f|--force) force=1;;
|
||||||
|
-u|--update) update=1;;
|
||||||
|
-p|--pep8) just_pep8=1;;
|
||||||
|
-P|--no-pep8) no_pep8=1;;
|
||||||
|
-c|--coverage) coverage=1;;
|
||||||
|
-d|--debug) debug=1;;
|
||||||
|
--virtual-env-path)
|
||||||
|
(( i++ ))
|
||||||
|
venv_path=${!i}
|
||||||
|
;;
|
||||||
|
--virtual-env-name)
|
||||||
|
(( i++ ))
|
||||||
|
venv_dir=${!i}
|
||||||
|
;;
|
||||||
|
--tools-path)
|
||||||
|
(( i++ ))
|
||||||
|
tools_path=${!i}
|
||||||
|
;;
|
||||||
|
-*) testropts="$testropts ${!i}";;
|
||||||
|
*) testrargs="$testrargs ${!i}"
|
||||||
|
esac
|
||||||
|
(( i++ ))
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
|
tool_path=${tools_path:-$(pwd)}
|
||||||
|
venv_path=${venv_path:-$(pwd)}
|
||||||
|
venv_dir=${venv_name:-.venv}
|
||||||
|
with_venv=tools/with_venv.sh
|
||||||
|
always_venv=0
|
||||||
|
never_venv=0
|
||||||
|
force=0
|
||||||
|
no_site_packages=0
|
||||||
|
installvenvopts=
|
||||||
|
testrargs=
|
||||||
|
testropts=
|
||||||
|
wrapper=""
|
||||||
|
just_pep8=0
|
||||||
|
no_pep8=0
|
||||||
|
coverage=0
|
||||||
|
debug=0
|
||||||
|
recreate_db=1
|
||||||
|
update=0
|
||||||
|
|
||||||
|
LANG=en_US.UTF-8
|
||||||
|
LANGUAGE=en_US:en
|
||||||
|
LC_ALL=C
|
||||||
|
|
||||||
|
process_options $@
|
||||||
|
# Make our paths available to other scripts we call
|
||||||
|
export venv_path
|
||||||
|
export venv_dir
|
||||||
|
export venv_name
|
||||||
|
export tools_dir
|
||||||
|
export venv=${venv_path}/${venv_dir}
|
||||||
|
|
||||||
|
if [ $no_site_packages -eq 1 ]; then
|
||||||
|
installvenvopts="--no-site-packages"
|
||||||
|
fi
|
||||||
|
|
||||||
|
|
||||||
|
function run_tests {
|
||||||
|
# Cleanup *pyc
|
||||||
|
${wrapper} find . -type f -name "*.pyc" -delete
|
||||||
|
|
||||||
|
if [ $debug -eq 1 ]; then
|
||||||
|
if [ "$testropts" = "" ] && [ "$testrargs" = "" ]; then
|
||||||
|
# Default to running all tests if specific test is not
|
||||||
|
# provided.
|
||||||
|
testrargs="discover ./gbp/"
|
||||||
|
fi
|
||||||
|
${wrapper} python -m testtools.run $testropts $testrargs
|
||||||
|
|
||||||
|
# Short circuit because all of the testr and coverage stuff
|
||||||
|
# below does not make sense when running testtools.run for
|
||||||
|
# debugging purposes.
|
||||||
|
return $?
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ $coverage -eq 1 ]; then
|
||||||
|
TESTRTESTS="$TESTRTESTS --coverage"
|
||||||
|
else
|
||||||
|
TESTRTESTS="$TESTRTESTS --slowest"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Just run the test suites in current environment
|
||||||
|
set +e
|
||||||
|
testrargs=`echo "$testrargs" | sed -e's/^\s*\(.*\)\s*$/\1/'`
|
||||||
|
TESTRTESTS="$TESTRTESTS --testr-args='--subunit $testropts $testrargs'"
|
||||||
|
OS_TEST_PATH=`echo $testrargs|grep -o 'gbp\.tests[^[:space:]:]*\+'|tr . /`
|
||||||
|
if [ -d "$OS_TEST_PATH" ]; then
|
||||||
|
wrapper="OS_TEST_PATH=$OS_TEST_PATH $wrapper"
|
||||||
|
elif [ -d "$(dirname $OS_TEST_PATH)" ]; then
|
||||||
|
wrapper="OS_TEST_PATH=$(dirname $OS_TEST_PATH) $wrapper"
|
||||||
|
fi
|
||||||
|
echo "Running \`${wrapper} $TESTRTESTS\`"
|
||||||
|
bash -c "${wrapper} $TESTRTESTS | ${wrapper} subunit2pyunit"
|
||||||
|
RESULT=$?
|
||||||
|
set -e
|
||||||
|
|
||||||
|
copy_subunit_log
|
||||||
|
|
||||||
|
if [ $coverage -eq 1 ]; then
|
||||||
|
echo "Generating coverage report in covhtml/"
|
||||||
|
# Don't compute coverage for common code, which is tested elsewhere
|
||||||
|
${wrapper} coverage combine
|
||||||
|
${wrapper} coverage html --include='gbp/*' --omit='gbp/openstack/common/*' -d covhtml -i
|
||||||
|
fi
|
||||||
|
|
||||||
|
return $RESULT
|
||||||
|
}
|
||||||
|
|
||||||
|
function copy_subunit_log {
|
||||||
|
LOGNAME=`cat .testrepository/next-stream`
|
||||||
|
LOGNAME=$(($LOGNAME - 1))
|
||||||
|
LOGNAME=".testrepository/${LOGNAME}"
|
||||||
|
cp $LOGNAME subunit.log
|
||||||
|
}
|
||||||
|
|
||||||
|
function run_pep8 {
|
||||||
|
echo "Running flake8 ..."
|
||||||
|
|
||||||
|
${wrapper} flake8
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
TESTRTESTS="python setup.py testr"
|
||||||
|
|
||||||
|
if [ $never_venv -eq 0 ]
|
||||||
|
then
|
||||||
|
# Remove the virtual environment if --force used
|
||||||
|
if [ $force -eq 1 ]; then
|
||||||
|
echo "Cleaning virtualenv..."
|
||||||
|
rm -rf ${venv}
|
||||||
|
fi
|
||||||
|
if [ $update -eq 1 ]; then
|
||||||
|
echo "Updating virtualenv..."
|
||||||
|
python tools/install_venv.py $installvenvopts
|
||||||
|
fi
|
||||||
|
if [ -e ${venv} ]; then
|
||||||
|
wrapper="${with_venv}"
|
||||||
|
else
|
||||||
|
if [ $always_venv -eq 1 ]; then
|
||||||
|
# Automatically install the virtualenv
|
||||||
|
python tools/install_venv.py $installvenvopts
|
||||||
|
wrapper="${with_venv}"
|
||||||
|
else
|
||||||
|
echo -e "No virtual environment found...create one? (Y/n) \c"
|
||||||
|
read use_ve
|
||||||
|
if [ "x$use_ve" = "xY" -o "x$use_ve" = "x" -o "x$use_ve" = "xy" ]; then
|
||||||
|
# Install the virtualenv and run the test suite in it
|
||||||
|
python tools/install_venv.py $installvenvopts
|
||||||
|
wrapper=${with_venv}
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Delete old coverage data from previous runs
|
||||||
|
if [ $coverage -eq 1 ]; then
|
||||||
|
${wrapper} coverage erase
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ $just_pep8 -eq 1 ]; then
|
||||||
|
run_pep8
|
||||||
|
exit
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ $recreate_db -eq 1 ]; then
|
||||||
|
rm -f tests.sqlite
|
||||||
|
fi
|
||||||
|
|
||||||
|
run_tests
|
||||||
|
|
||||||
|
# NOTE(sirp): we only want to run pep8 when we're running the full-test suite,
|
||||||
|
# not when we're running tests individually. To handle this, we need to
|
||||||
|
# distinguish between options (testropts), which begin with a '-', and
|
||||||
|
# arguments (testrargs).
|
||||||
|
if [ -z "$testrargs" ]; then
|
||||||
|
if [ $no_pep8 -eq 0 ]; then
|
||||||
|
run_pep8
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
@ -28,7 +28,7 @@ setup-hooks =
|
|||||||
|
|
||||||
[entry_points]
|
[entry_points]
|
||||||
console_scripts =
|
console_scripts =
|
||||||
gbp = gbpclient.shell:main
|
gbp = gbpclient.gbpshell:main
|
||||||
|
|
||||||
[build_sphinx]
|
[build_sphinx]
|
||||||
all_files = 1
|
all_files = 1
|
||||||
|
@ -15,4 +15,5 @@ oslotest>=1.1.0.0a2
|
|||||||
python-subunit>=0.0.18
|
python-subunit>=0.0.18
|
||||||
sphinx>=1.1.2,!=1.2.0,<1.3
|
sphinx>=1.1.2,!=1.2.0,<1.3
|
||||||
testrepository>=0.0.18
|
testrepository>=0.0.18
|
||||||
|
testscenarios>=0.4
|
||||||
testtools>=0.9.34
|
testtools>=0.9.34
|
||||||
|
74
tools/install_venv.py
Normal file
74
tools/install_venv.py
Normal file
@ -0,0 +1,74 @@
|
|||||||
|
# Copyright 2010 United States Government as represented by the
|
||||||
|
# Administrator of the National Aeronautics and Space Administration.
|
||||||
|
# All Rights Reserved.
|
||||||
|
#
|
||||||
|
# Copyright 2010 OpenStack Foundation
|
||||||
|
# Copyright 2013 IBM Corp.
|
||||||
|
# Copyright (c) 2013 Hewlett-Packard Development Company, L.P.
|
||||||
|
#
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||||
|
# not use this file except in compliance with the License. You may obtain
|
||||||
|
# a copy of the License at
|
||||||
|
#
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||||
|
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||||
|
# License for the specific language governing permissions and limitations
|
||||||
|
# under the License.
|
||||||
|
|
||||||
|
import ConfigParser
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
|
||||||
|
import install_venv_common as install_venv # flake8: noqa
|
||||||
|
|
||||||
|
|
||||||
|
def print_help(project, venv, root):
|
||||||
|
help = """
|
||||||
|
%(project)s development environment setup is complete.
|
||||||
|
|
||||||
|
%(project)s development uses virtualenv to track and manage Python
|
||||||
|
dependencies while in development and testing.
|
||||||
|
|
||||||
|
To activate the %(project)s virtualenv for the extent of your current
|
||||||
|
shell session you can run:
|
||||||
|
|
||||||
|
$ source %(venv)s/bin/activate
|
||||||
|
|
||||||
|
Or, if you prefer, you can run commands in the virtualenv on a case by
|
||||||
|
case basis by running:
|
||||||
|
|
||||||
|
$ %(root)s/tools/with_venv.sh <your command>
|
||||||
|
"""
|
||||||
|
print help % dict(project=project, venv=venv, root=root)
|
||||||
|
|
||||||
|
|
||||||
|
def main(argv):
|
||||||
|
root = os.path.dirname(os.path.dirname(os.path.realpath(__file__)))
|
||||||
|
|
||||||
|
if os.environ.get('tools_path'):
|
||||||
|
root = os.environ['tools_path']
|
||||||
|
venv = os.path.join(root, '.venv')
|
||||||
|
if os.environ.get('venv'):
|
||||||
|
venv = os.environ['venv']
|
||||||
|
|
||||||
|
pip_requires = os.path.join(root, 'requirements.txt')
|
||||||
|
test_requires = os.path.join(root, 'test-requirements.txt')
|
||||||
|
py_version = "python%s.%s" % (sys.version_info[0], sys.version_info[1])
|
||||||
|
setup_cfg = ConfigParser.ConfigParser()
|
||||||
|
setup_cfg.read('setup.cfg')
|
||||||
|
project = setup_cfg.get('metadata', 'name')
|
||||||
|
|
||||||
|
install = install_venv.InstallVenv(
|
||||||
|
root, venv, pip_requires, test_requires, py_version, project)
|
||||||
|
options = install.parse_args(argv)
|
||||||
|
install.check_python_version()
|
||||||
|
install.check_dependencies()
|
||||||
|
install.create_virtualenv(no_site_packages=options.no_site_packages)
|
||||||
|
install.install_dependencies()
|
||||||
|
print_help(project, venv, root)
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
main(sys.argv)
|
172
tools/install_venv_common.py
Normal file
172
tools/install_venv_common.py
Normal file
@ -0,0 +1,172 @@
|
|||||||
|
# Copyright 2013 OpenStack Foundation
|
||||||
|
# Copyright 2013 IBM Corp.
|
||||||
|
#
|
||||||
|
# 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.
|
||||||
|
|
||||||
|
"""Provides methods needed by installation script for OpenStack development
|
||||||
|
virtual environments.
|
||||||
|
|
||||||
|
Since this script is used to bootstrap a virtualenv from the system's Python
|
||||||
|
environment, it should be kept strictly compatible with Python 2.6.
|
||||||
|
|
||||||
|
Synced in from openstack-common
|
||||||
|
"""
|
||||||
|
|
||||||
|
from __future__ import print_function
|
||||||
|
|
||||||
|
import optparse
|
||||||
|
import os
|
||||||
|
import subprocess
|
||||||
|
import sys
|
||||||
|
|
||||||
|
|
||||||
|
class InstallVenv(object):
|
||||||
|
|
||||||
|
def __init__(self, root, venv, requirements,
|
||||||
|
test_requirements, py_version,
|
||||||
|
project):
|
||||||
|
self.root = root
|
||||||
|
self.venv = venv
|
||||||
|
self.requirements = requirements
|
||||||
|
self.test_requirements = test_requirements
|
||||||
|
self.py_version = py_version
|
||||||
|
self.project = project
|
||||||
|
|
||||||
|
def die(self, message, *args):
|
||||||
|
print(message % args, file=sys.stderr)
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
def check_python_version(self):
|
||||||
|
if sys.version_info < (2, 6):
|
||||||
|
self.die("Need Python Version >= 2.6")
|
||||||
|
|
||||||
|
def run_command_with_code(self, cmd, redirect_output=True,
|
||||||
|
check_exit_code=True):
|
||||||
|
"""Runs a command in an out-of-process shell.
|
||||||
|
|
||||||
|
Returns the output of that command. Working directory is self.root.
|
||||||
|
"""
|
||||||
|
if redirect_output:
|
||||||
|
stdout = subprocess.PIPE
|
||||||
|
else:
|
||||||
|
stdout = None
|
||||||
|
|
||||||
|
proc = subprocess.Popen(cmd, cwd=self.root, stdout=stdout)
|
||||||
|
output = proc.communicate()[0]
|
||||||
|
if check_exit_code and proc.returncode != 0:
|
||||||
|
self.die('Command "%s" failed.\n%s', ' '.join(cmd), output)
|
||||||
|
return (output, proc.returncode)
|
||||||
|
|
||||||
|
def run_command(self, cmd, redirect_output=True, check_exit_code=True):
|
||||||
|
return self.run_command_with_code(cmd, redirect_output,
|
||||||
|
check_exit_code)[0]
|
||||||
|
|
||||||
|
def get_distro(self):
|
||||||
|
if (os.path.exists('/etc/fedora-release') or
|
||||||
|
os.path.exists('/etc/redhat-release')):
|
||||||
|
return Fedora(
|
||||||
|
self.root, self.venv, self.requirements,
|
||||||
|
self.test_requirements, self.py_version, self.project)
|
||||||
|
else:
|
||||||
|
return Distro(
|
||||||
|
self.root, self.venv, self.requirements,
|
||||||
|
self.test_requirements, self.py_version, self.project)
|
||||||
|
|
||||||
|
def check_dependencies(self):
|
||||||
|
self.get_distro().install_virtualenv()
|
||||||
|
|
||||||
|
def create_virtualenv(self, no_site_packages=True):
|
||||||
|
"""Creates the virtual environment and installs PIP.
|
||||||
|
|
||||||
|
Creates the virtual environment and installs PIP only into the
|
||||||
|
virtual environment.
|
||||||
|
"""
|
||||||
|
if not os.path.isdir(self.venv):
|
||||||
|
print('Creating venv...', end=' ')
|
||||||
|
if no_site_packages:
|
||||||
|
self.run_command(['virtualenv', '-q', '--no-site-packages',
|
||||||
|
self.venv])
|
||||||
|
else:
|
||||||
|
self.run_command(['virtualenv', '-q', self.venv])
|
||||||
|
print('done.')
|
||||||
|
else:
|
||||||
|
print("venv already exists...")
|
||||||
|
pass
|
||||||
|
|
||||||
|
def pip_install(self, *args):
|
||||||
|
self.run_command(['tools/with_venv.sh',
|
||||||
|
'pip', 'install', '--upgrade'] + list(args),
|
||||||
|
redirect_output=False)
|
||||||
|
|
||||||
|
def install_dependencies(self):
|
||||||
|
print('Installing dependencies with pip (this can take a while)...')
|
||||||
|
|
||||||
|
# First things first, make sure our venv has the latest pip and
|
||||||
|
# setuptools and pbr
|
||||||
|
self.pip_install('pip>=1.4')
|
||||||
|
self.pip_install('setuptools')
|
||||||
|
self.pip_install('pbr')
|
||||||
|
|
||||||
|
self.pip_install('-r', self.requirements, '-r', self.test_requirements)
|
||||||
|
|
||||||
|
def parse_args(self, argv):
|
||||||
|
"""Parses command-line arguments."""
|
||||||
|
parser = optparse.OptionParser()
|
||||||
|
parser.add_option('-n', '--no-site-packages',
|
||||||
|
action='store_true',
|
||||||
|
help="Do not inherit packages from global Python "
|
||||||
|
"install.")
|
||||||
|
return parser.parse_args(argv[1:])[0]
|
||||||
|
|
||||||
|
|
||||||
|
class Distro(InstallVenv):
|
||||||
|
|
||||||
|
def check_cmd(self, cmd):
|
||||||
|
return bool(self.run_command(['which', cmd],
|
||||||
|
check_exit_code=False).strip())
|
||||||
|
|
||||||
|
def install_virtualenv(self):
|
||||||
|
if self.check_cmd('virtualenv'):
|
||||||
|
return
|
||||||
|
|
||||||
|
if self.check_cmd('easy_install'):
|
||||||
|
print('Installing virtualenv via easy_install...', end=' ')
|
||||||
|
if self.run_command(['easy_install', 'virtualenv']):
|
||||||
|
print('Succeeded')
|
||||||
|
return
|
||||||
|
else:
|
||||||
|
print('Failed')
|
||||||
|
|
||||||
|
self.die('ERROR: virtualenv not found.\n\n%s development'
|
||||||
|
' requires virtualenv, please install it using your'
|
||||||
|
' favorite package management tool' % self.project)
|
||||||
|
|
||||||
|
|
||||||
|
class Fedora(Distro):
|
||||||
|
"""This covers all Fedora-based distributions.
|
||||||
|
|
||||||
|
Includes: Fedora, RHEL, CentOS, Scientific Linux
|
||||||
|
"""
|
||||||
|
|
||||||
|
def check_pkg(self, pkg):
|
||||||
|
return self.run_command_with_code(['rpm', '-q', pkg],
|
||||||
|
check_exit_code=False)[1] == 0
|
||||||
|
|
||||||
|
def install_virtualenv(self):
|
||||||
|
if self.check_cmd('virtualenv'):
|
||||||
|
return
|
||||||
|
|
||||||
|
if not self.check_pkg('python-virtualenv'):
|
||||||
|
self.die("Please install 'python-virtualenv'.")
|
||||||
|
|
||||||
|
super(Fedora, self).install_virtualenv()
|
@ -1,27 +0,0 @@
|
|||||||
_policy_opts="" # lazy init
|
|
||||||
_policy_flags="" # lazy init
|
|
||||||
_policy_opts_exp="" # lazy init
|
|
||||||
_policy()
|
|
||||||
{
|
|
||||||
local cur prev nbc cflags
|
|
||||||
COMPREPLY=()
|
|
||||||
cur="${COMP_WORDS[COMP_CWORD]}"
|
|
||||||
prev="${COMP_WORDS[COMP_CWORD-1]}"
|
|
||||||
|
|
||||||
if [ "x$_policy_opts" == "x" ] ; then
|
|
||||||
nbc="`policy bash-completion`"
|
|
||||||
_policy_opts="`echo "$nbc" | sed -e "s/--[a-z0-9_-]*//g" -e "s/\s\s*/ /g"`"
|
|
||||||
_policy_flags="`echo " $nbc" | sed -e "s/ [^-][^-][a-z0-9_-]*//g" -e "s/\s\s*/ /g"`"
|
|
||||||
_policy_opts_exp="`echo "$_policy_opts" | sed -e "s/\s/|/g"`"
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [[ " ${COMP_WORDS[@]} " =~ " "($_policy_opts_exp)" " && "$prev" != "help" ]] ; then
|
|
||||||
COMPLETION_CACHE=~/.policyclient/*/*-cache
|
|
||||||
cflags="$_policy_flags "$(cat $COMPLETION_CACHE 2> /dev/null | tr '\n' ' ')
|
|
||||||
COMPREPLY=($(compgen -W "${cflags}" -- ${cur}))
|
|
||||||
else
|
|
||||||
COMPREPLY=($(compgen -W "${_policy_opts}" -- ${cur}))
|
|
||||||
fi
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
complete -F _policy policy
|
|
4
tools/with_venv.sh
Executable file
4
tools/with_venv.sh
Executable file
@ -0,0 +1,4 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
TOOLS=`dirname $0`
|
||||||
|
VENV=$TOOLS/../.venv
|
||||||
|
source $VENV/bin/activate && $@
|
Loading…
x
Reference in New Issue
Block a user