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
|
||||
run_tests.err.log
|
||||
run_tests.log
|
||||
subunit.log
|
||||
.autogenerated
|
||||
.coverage
|
||||
.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 v3 as ks_v3_fixture
|
||||
from keystoneclient import session
|
||||
|
||||
from neutronclient import client
|
||||
from neutronclient.common import exceptions
|
||||
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
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
@ -14,198 +11,56 @@
|
||||
# under the License.
|
||||
#
|
||||
|
||||
import contextlib
|
||||
import itertools
|
||||
import sys
|
||||
import urllib
|
||||
|
||||
import fixtures
|
||||
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.neutron import v2_0 as neutronV2_0
|
||||
from neutronclient import shell
|
||||
from neutronclient.v2_0 import client
|
||||
from neutronclient.tests.unit import test_cli20 as neutron_test_cli20
|
||||
import requests
|
||||
|
||||
API_VERSION = "2.0"
|
||||
FORMAT = 'json'
|
||||
TOKEN = 'testtoken'
|
||||
ENDURL = 'localurl'
|
||||
from gbpclient.gbp import v2_0 as gbpV2_0
|
||||
from gbpclient import gbpshell
|
||||
from gbpclient.v2_0 import client as gbpclient
|
||||
|
||||
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
|
||||
def capture_std_streams():
|
||||
fake_stdout, fake_stderr = six.StringIO(), six.StringIO()
|
||||
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(neutron_test_cli20.FakeStdout):
|
||||
|
||||
pass
|
||||
|
||||
|
||||
class FakeStdout:
|
||||
class MyResp(neutron_test_cli20.MyResp):
|
||||
|
||||
def __init__(self):
|
||||
self.content = []
|
||||
|
||||
def write(self, text):
|
||||
self.content.append(text)
|
||||
|
||||
def make_string(self):
|
||||
result = ''
|
||||
for line in self.content:
|
||||
result = result + line
|
||||
return result
|
||||
pass
|
||||
|
||||
|
||||
class MyResp(object):
|
||||
def __init__(self, status_code, headers=None, reason=None):
|
||||
self.status_code = status_code
|
||||
self.headers = headers or {}
|
||||
self.reason = reason
|
||||
class MyApp(neutron_test_cli20.MyApp):
|
||||
|
||||
pass
|
||||
|
||||
|
||||
class MyApp(object):
|
||||
def __init__(self, _stdout):
|
||||
self.stdout = _stdout
|
||||
class MyUrlComparator(neutron_test_cli20.MyUrlComparator):
|
||||
|
||||
pass
|
||||
|
||||
|
||||
def end_url(path, query=None, format=FORMAT):
|
||||
_url_str = ENDURL + "/v" + API_VERSION + path + "." + format
|
||||
return query and _url_str + "?" + query or _url_str
|
||||
class MyComparator(neutron_test_cli20.MyComparator):
|
||||
|
||||
pass
|
||||
|
||||
|
||||
class MyUrlComparator(mox.Comparator):
|
||||
def __init__(self, lhs, client):
|
||||
self.lhs = lhs
|
||||
self.client = client
|
||||
class CLITestV20Base(neutron_test_cli20.CLITestV20Base):
|
||||
|
||||
def equals(self, rhs):
|
||||
lhsp = urlparse.urlparse(self.lhs)
|
||||
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'}}
|
||||
shell = gbpshell
|
||||
client = gbpclient
|
||||
|
||||
def setUp(self, plurals=None):
|
||||
"""Prepare the test environment."""
|
||||
super(CLITestV20Base, self).setUp()
|
||||
client.Client.EXTED_PLURALS.update(constants.PLURALS)
|
||||
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)
|
||||
self.client = gbpclient.Client(token=TOKEN, endpoint_url=self.endurl)
|
||||
|
||||
def _test_create_resource(self, resource, cmd, name, myid, args,
|
||||
position_names, position_values,
|
||||
@ -215,19 +70,9 @@ class CLITestV20Base(base.BaseTestCase):
|
||||
self.mox.StubOutWithMock(cmd, "get_client")
|
||||
self.mox.StubOutWithMock(self.client.httpclient, "request")
|
||||
cmd.get_client().MultipleTimes().AndReturn(self.client)
|
||||
non_admin_status_resources = ['subnet', 'floatingip', 'security_group',
|
||||
'security_group_rule', 'qos_queue',
|
||||
'network_gateway', 'gateway_device',
|
||||
'credential', 'network_profile',
|
||||
'policy_profile', 'ikepolicy',
|
||||
'ipsecpolicy', 'metering_label',
|
||||
'metering_label_rule', 'net_partition']
|
||||
if not cmd_resource:
|
||||
cmd_resource = resource
|
||||
if (resource in non_admin_status_resources):
|
||||
body = {resource: {}, }
|
||||
else:
|
||||
body = {resource: {'admin_state_up': admin_state_up, }, }
|
||||
body = {resource: {}, }
|
||||
if tenant_id:
|
||||
body[resource].update({'tenant_id': tenant_id})
|
||||
if tags:
|
||||
@ -245,8 +90,8 @@ class CLITestV20Base(base.BaseTestCase):
|
||||
self.client.format = self.format
|
||||
resstr = self.client.serialize(ress)
|
||||
# url method body
|
||||
resource_plural = neutronV2_0._get_resource_plural(cmd_resource,
|
||||
self.client)
|
||||
resource_plural = gbpV2_0._get_resource_plural(cmd_resource,
|
||||
self.client)
|
||||
path = getattr(self.client, resource_plural + "_path")
|
||||
if parent_id:
|
||||
path = path % parent_id
|
||||
@ -264,7 +109,7 @@ class CLITestV20Base(base.BaseTestCase):
|
||||
args.extend(['--request-format', self.format])
|
||||
self.mox.ReplayAll()
|
||||
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.UnsetStubs()
|
||||
_str = self.fake_stdout.make_string()
|
||||
@ -272,354 +117,10 @@ class CLITestV20Base(base.BaseTestCase):
|
||||
if name:
|
||||
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):
|
||||
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(
|
||||
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'
|
||||
pass
|
||||
|
||||
|
||||
class CLITestV20ExceptionHandler(CLITestV20Base):
|
||||
@ -634,7 +135,7 @@ class CLITestV20ExceptionHandler(CLITestV20Base):
|
||||
'detail': error_detail}}
|
||||
|
||||
e = self.assertRaises(expected_exception,
|
||||
client.exception_handler_v20,
|
||||
gbpclient.exception_handler_v20,
|
||||
status_code, error_content)
|
||||
self.assertEqual(status_code, e.status_code)
|
||||
|
||||
@ -645,44 +146,13 @@ class CLITestV20ExceptionHandler(CLITestV20Base):
|
||||
expected_msg = error_msg
|
||||
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):
|
||||
known_error_map = [
|
||||
('NetworkNotFound', exceptions.NetworkNotFoundClient, 404),
|
||||
('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)
|
||||
# TODO(Sumit): This needs to be adapted for GBP
|
||||
pass
|
||||
|
||||
def test_exception_handler_v20_neutron_known_error_without_detail(self):
|
||||
error_msg = 'Network not found'
|
||||
error_detail = ''
|
||||
self._test_exception_handler_v20(
|
||||
exceptions.NetworkNotFoundClient, 404,
|
||||
error_msg,
|
||||
'NetworkNotFound', error_msg, error_detail)
|
||||
# TODO(Sumit): This needs to be adapted for GBP
|
||||
pass
|
||||
|
||||
def test_exception_handler_v20_unknown_error_to_per_code_exception(self):
|
||||
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]
|
||||
console_scripts =
|
||||
gbp = gbpclient.shell:main
|
||||
gbp = gbpclient.gbpshell:main
|
||||
|
||||
[build_sphinx]
|
||||
all_files = 1
|
||||
|
@ -15,4 +15,5 @@ oslotest>=1.1.0.0a2
|
||||
python-subunit>=0.0.18
|
||||
sphinx>=1.1.2,!=1.2.0,<1.3
|
||||
testrepository>=0.0.18
|
||||
testscenarios>=0.4
|
||||
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…
Reference in New Issue
Block a user