summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--doc/source/usage/osc/v2/network-trunk.rst10
-rw-r--r--neutronclient/neutron/v2_0/quota.py4
-rw-r--r--neutronclient/neutron/v2_0/router.py7
-rw-r--r--neutronclient/osc/v2/trunk/network_trunk.py22
-rw-r--r--neutronclient/tests/unit/osc/v2/trunk/fakes.py5
-rw-r--r--neutronclient/tests/unit/osc/v2/trunk/test_network_trunk.py63
-rw-r--r--neutronclient/tests/unit/test_cli20_router.py13
-rw-r--r--neutronclient/tests/unit/test_quota.py8
-rw-r--r--releasenotes/notes/fix-quota-update-zero-args-d596c4169c2d2e30.yaml9
-rw-r--r--requirements.txt2
-rwxr-xr-xtools/tox_install.sh66
11 files changed, 154 insertions, 55 deletions
diff --git a/doc/source/usage/osc/v2/network-trunk.rst b/doc/source/usage/osc/v2/network-trunk.rst
index 5a79310..a39e3bd 100644
--- a/doc/source/usage/osc/v2/network-trunk.rst
+++ b/doc/source/usage/osc/v2/network-trunk.rst
@@ -37,6 +37,7 @@ Create a network trunk for a given project
[--subport <port=,segmentation-type=,segmentation-id=>]
[--enable | --disable]
[--project <project> [--project-domain <project-domain>]]
+ [--description <description>]
<name>
.. option:: --parent-port <parent-port>
@@ -65,6 +66,10 @@ Create a network trunk for a given project
Domain the project belongs to (name or ID).
This can be used in case collisions between project names exist.
+.. option:: --description <description>
+
+ A description of the trunk.
+
network trunk delete
--------------------
@@ -106,6 +111,7 @@ Set network trunk properties
os network trunk set
[--name <name>]
+ [--description <description>]
[--subport <port=,segmentation-type=,segmentation-id=>]
[--enable | --disable]
<trunk>
@@ -114,6 +120,10 @@ Set network trunk properties
Set trunk name
+.. option:: --description <description>
+
+ A description of the trunk.
+
.. option:: --subport <port=,segmentation-type=,segmentation-id=>
Subport to add. Subport is of form 'port=<name or ID>,segmentation-type=,segmentation-ID='
diff --git a/neutronclient/neutron/v2_0/quota.py b/neutronclient/neutron/v2_0/quota.py
index ad36194..160b6e4 100644
--- a/neutronclient/neutron/v2_0/quota.py
+++ b/neutronclient/neutron/v2_0/quota.py
@@ -216,6 +216,10 @@ class UpdateQuota(neutronV20.NeutronCommand, show.ShowOne):
quota[resource] = self._validate_int(
resource,
getattr(parsed_args, resource))
+ if not quota:
+ raise exceptions.CommandError(
+ message=_('Must specify a valid resource with new quota '
+ 'value'))
return {self.resource: quota}
def take_action(self, parsed_args):
diff --git a/neutronclient/neutron/v2_0/router.py b/neutronclient/neutron/v2_0/router.py
index 200feee..b431daa 100644
--- a/neutronclient/neutron/v2_0/router.py
+++ b/neutronclient/neutron/v2_0/router.py
@@ -71,6 +71,9 @@ class CreateRouter(neutronV20.CreateCommand):
parser.add_argument(
'--description',
help=_('Description of router.'))
+ parser.add_argument(
+ '--flavor',
+ help=_('ID or name of flavor.'))
utils.add_boolean_argument(
parser, '--distributed', dest='distributed',
help=_('Create a distributed router.'))
@@ -82,6 +85,10 @@ class CreateRouter(neutronV20.CreateCommand):
def args2body(self, parsed_args):
body = {'admin_state_up': parsed_args.admin_state}
+ if parsed_args.flavor:
+ _flavor_id = neutronV20.find_resourceid_by_name_or_id(
+ self.get_client(), 'flavor', parsed_args.flavor)
+ body['flavor_id'] = _flavor_id
neutronV20.update_dict(parsed_args, body,
['name', 'tenant_id', 'distributed', 'ha',
'description'])
diff --git a/neutronclient/osc/v2/trunk/network_trunk.py b/neutronclient/osc/v2/trunk/network_trunk.py
index e3eed82..70f791f 100644
--- a/neutronclient/osc/v2/trunk/network_trunk.py
+++ b/neutronclient/osc/v2/trunk/network_trunk.py
@@ -45,6 +45,11 @@ class CreateNetworkTrunk(command.ShowOne):
help=_("Name of the trunk to create")
)
parser.add_argument(
+ '--description',
+ metavar='<description>',
+ help=_("A description of the trunk")
+ )
+ parser.add_argument(
'--parent-port',
metavar='<parent-port>',
required=True,
@@ -141,21 +146,27 @@ class ListNetworkTrunk(command.Lister):
headers = (
'ID',
'Name',
- 'Parent Port'
+ 'Parent Port',
+ 'Description'
)
columns = (
'id',
'name',
- 'port_id'
+ 'port_id',
+ 'description'
)
if parsed_args.long:
headers += (
'Status',
'State',
+ 'Created At',
+ 'Updated At',
)
columns += (
'status',
'admin_state_up',
+ 'created_at',
+ 'updated_at'
)
return (headers,
(utils.get_dict_properties(
@@ -180,6 +191,11 @@ class SetNetworkTrunk(command.Command):
help=_("Set trunk name")
)
parser.add_argument(
+ '--description',
+ metavar='<description>',
+ help=_("A description of the trunk")
+ )
+ parser.add_argument(
'--subport',
metavar='<port=,segmentation-type=,segmentation-id=>',
action=parseractions.MultiKeyValueAction, dest='set_subports',
@@ -313,6 +329,8 @@ def _get_attrs_for_trunk(client_manager, parsed_args):
attrs = {}
if parsed_args.name is not None:
attrs['name'] = str(parsed_args.name)
+ if parsed_args.description is not None:
+ attrs['description'] = str(parsed_args.description)
if parsed_args.enable:
attrs['admin_state_up'] = True
if parsed_args.disable:
diff --git a/neutronclient/tests/unit/osc/v2/trunk/fakes.py b/neutronclient/tests/unit/osc/v2/trunk/fakes.py
index 0eb6f97..1acbcc3 100644
--- a/neutronclient/tests/unit/osc/v2/trunk/fakes.py
+++ b/neutronclient/tests/unit/osc/v2/trunk/fakes.py
@@ -24,8 +24,8 @@ class FakeTrunk(object):
:param Dictionary attrs:
A dictionary with all attributes
:return:
- A Dictionary with id, name, admin_state_up,
- port_id, sub_ports, status and project_id
+ A Dictionary with id, name, description, admin_state_up, port_id,
+ sub_ports, status and project_id
"""
attrs = attrs or {}
@@ -33,6 +33,7 @@ class FakeTrunk(object):
trunk_attrs = {
'id': 'trunk-id-' + uuid.uuid4().hex,
'name': 'trunk-name-' + uuid.uuid4().hex,
+ 'description': '',
'port_id': 'port-' + uuid.uuid4().hex,
'admin_state_up': True,
'project_id': 'project-id-' + uuid.uuid4().hex,
diff --git a/neutronclient/tests/unit/osc/v2/trunk/test_network_trunk.py b/neutronclient/tests/unit/osc/v2/trunk/test_network_trunk.py
index 5bb5ea2..d965815 100644
--- a/neutronclient/tests/unit/osc/v2/trunk/test_network_trunk.py
+++ b/neutronclient/tests/unit/osc/v2/trunk/test_network_trunk.py
@@ -36,6 +36,7 @@ class TestCreateNetworkTrunk(test_fakes.TestNeutronClientOSCV2):
columns = (
'admin_state_up',
+ 'description',
'id',
'name',
'port_id',
@@ -43,15 +44,18 @@ class TestCreateNetworkTrunk(test_fakes.TestNeutronClientOSCV2):
'status',
'sub_ports',
)
- data = (
- trunk._format_admin_state(_trunk['admin_state_up']),
- _trunk['id'],
- _trunk['name'],
- _trunk['port_id'],
- _trunk['project_id'],
- _trunk['status'],
- utils.format_list_of_dicts(_trunk['sub_ports']),
- )
+
+ def get_data(self):
+ return (
+ trunk._format_admin_state(self._trunk['admin_state_up']),
+ self._trunk['description'],
+ self._trunk['id'],
+ self._trunk['name'],
+ self._trunk['port_id'],
+ self._trunk['project_id'],
+ self._trunk['status'],
+ utils.format_list_of_dicts(self._trunk['sub_ports']),
+ )
def setUp(self):
super(TestCreateNetworkTrunk, self).setUp()
@@ -59,6 +63,7 @@ class TestCreateNetworkTrunk(test_fakes.TestNeutronClientOSCV2):
new=_get_id).start()
self.neutronclient.create_trunk = mock.Mock(
return_value={trunk.TRUNK: self._trunk})
+ self.data = self.get_data()
# Get the command object to test
self.cmd = trunk.CreateNetworkTrunk(self.app, self.namespace)
@@ -92,9 +97,12 @@ class TestCreateNetworkTrunk(test_fakes.TestNeutronClientOSCV2):
self.assertEqual(self.data, data)
def test_create_full_options(self):
+ self._trunk['description'] = 'foo description'
+ self.data = self.get_data()
subport = self._trunk['sub_ports'][0]
arglist = [
"--disable",
+ "--description", self._trunk['description'],
"--parent-port", self._trunk['port_id'],
"--subport", 'port=%(port)s,segmentation-type=%(seg_type)s,'
'segmentation-id=%(seg_id)s' % {
@@ -105,6 +113,7 @@ class TestCreateNetworkTrunk(test_fakes.TestNeutronClientOSCV2):
]
verifylist = [
('name', self._trunk['name']),
+ ('description', self._trunk['description']),
('parent_port', self._trunk['port_id']),
('add_subports', [{
'port': subport['port_id'],
@@ -119,6 +128,7 @@ class TestCreateNetworkTrunk(test_fakes.TestNeutronClientOSCV2):
self.neutronclient.create_trunk.assert_called_once_with({
trunk.TRUNK: {'name': self._trunk['name'],
+ 'description': self._trunk['description'],
'admin_state_up': False,
'sub_ports': [subport],
'port_id': self._trunk['port_id']}
@@ -229,6 +239,7 @@ class TestShowNetworkTrunk(test_fakes.TestNeutronClientOSCV2):
columns = (
'admin_state_up',
+ 'description',
'id',
'name',
'port_id',
@@ -238,6 +249,7 @@ class TestShowNetworkTrunk(test_fakes.TestNeutronClientOSCV2):
)
data = (
trunk._format_admin_state(_trunk['admin_state_up']),
+ _trunk['description'],
_trunk['id'],
_trunk['name'],
_trunk['port_id'],
@@ -283,16 +295,21 @@ class TestShowNetworkTrunk(test_fakes.TestNeutronClientOSCV2):
class TestListNetworkTrunk(test_fakes.TestNeutronClientOSCV2):
# Create trunks to be listed.
- _trunks = fakes.FakeTrunk.create_trunks(count=3)
+ _trunks = fakes.FakeTrunk.create_trunks(
+ {'created_at': '2001-01-01 00:00:00',
+ 'updated_at': '2001-01-01 00:00:00'}, count=3)
columns = (
'ID',
'Name',
'Parent Port',
+ 'Description'
)
columns_long = columns + (
'Status',
'State',
+ 'Created At',
+ 'Updated At'
)
data = []
for t in _trunks:
@@ -300,6 +317,7 @@ class TestListNetworkTrunk(test_fakes.TestNeutronClientOSCV2):
t['id'],
t['name'],
t['port_id'],
+ t['description']
))
data_long = []
for t in _trunks:
@@ -307,8 +325,11 @@ class TestListNetworkTrunk(test_fakes.TestNeutronClientOSCV2):
t['id'],
t['name'],
t['port_id'],
+ t['description'],
t['status'],
trunk._format_admin_state(t['admin_state_up']),
+ '2001-01-01 00:00:00',
+ '2001-01-01 00:00:00',
))
def setUp(self):
@@ -356,6 +377,7 @@ class TestSetNetworkTrunk(test_fakes.TestNeutronClientOSCV2):
'admin_state_up',
'id',
'name',
+ 'description',
'port_id',
'project_id',
'status',
@@ -365,6 +387,7 @@ class TestSetNetworkTrunk(test_fakes.TestNeutronClientOSCV2):
trunk._format_admin_state(_trunk['admin_state_up']),
_trunk['id'],
_trunk['name'],
+ _trunk['description'],
_trunk['port_id'],
_trunk['project_id'],
_trunk['status'],
@@ -383,26 +406,32 @@ class TestSetNetworkTrunk(test_fakes.TestNeutronClientOSCV2):
# Get the command object to test
self.cmd = trunk.SetNetworkTrunk(self.app, self.namespace)
- def test_set_network_trunk_name(self):
+ def _test_set_network_trunk_attr(self, attr, value):
arglist = [
- '--name', 'trunky',
- self._trunk['name'],
+ '--%s' % attr, value,
+ self._trunk[attr],
]
verifylist = [
- ('name', 'trunky'),
- ('trunk', self._trunk['name']),
+ (attr, value),
+ ('trunk', self._trunk[attr]),
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
result = self.cmd.take_action(parsed_args)
attrs = {
- 'name': 'trunky',
+ attr: value,
}
self.neutronclient.update_trunk.assert_called_once_with(
- self._trunk['name'], {trunk.TRUNK: attrs})
+ self._trunk[attr], {trunk.TRUNK: attrs})
self.assertIsNone(result)
+ def test_set_network_trunk_name(self):
+ self._test_set_network_trunk_attr('name', 'trunky')
+
+ def test_test_set_network_trunk_description(self):
+ self._test_set_network_trunk_attr('description', 'description')
+
def test_set_network_trunk_admin_state_up_disable(self):
arglist = [
'--disable',
diff --git a/neutronclient/tests/unit/test_cli20_router.py b/neutronclient/tests/unit/test_cli20_router.py
index cd7ed03..1ca818d 100644
--- a/neutronclient/tests/unit/test_cli20_router.py
+++ b/neutronclient/tests/unit/test_cli20_router.py
@@ -35,6 +35,19 @@ class CLITestV20RouterJSON(test_cli20.CLITestV20Base):
position_names, position_values,
description='rooter')
+ def test_create_router_flavor(self):
+ resource = 'router'
+ cmd = router.CreateRouter(test_cli20.MyApp(sys.stdout), None)
+ name = 'router1'
+ myid = 'myid'
+ flavor = 'router-flavor'
+ args = [name, '--flavor', flavor]
+ position_names = ['name', ]
+ position_values = [name, flavor]
+ self._test_create_resource(resource, cmd, name, myid, args,
+ position_names, position_values,
+ flavor_id='router-flavor')
+
def test_create_router_tenant(self):
# Create router: --tenant_id tenantid myname.
resource = 'router'
diff --git a/neutronclient/tests/unit/test_quota.py b/neutronclient/tests/unit/test_quota.py
index b9d7b44..78f4dae 100644
--- a/neutronclient/tests/unit/test_quota.py
+++ b/neutronclient/tests/unit/test_quota.py
@@ -92,3 +92,11 @@ class CLITestV20Quota(test_cli20.CLITestV20Base):
self.assertIn('subnet', _str)
self.assertIn('port', _str)
self.assertNotIn('subnetpool', _str)
+
+ def test_update_quota_noargs(self):
+ resource = 'quota'
+ cmd = test_quota.UpdateQuota(test_cli20.MyApp(sys.stdout), None)
+ args = [self.test_id]
+ self.assertRaises(exceptions.CommandError, self._test_update_resource,
+ resource, cmd, self.test_id, args=args,
+ extrafields=None)
diff --git a/releasenotes/notes/fix-quota-update-zero-args-d596c4169c2d2e30.yaml b/releasenotes/notes/fix-quota-update-zero-args-d596c4169c2d2e30.yaml
new file mode 100644
index 0000000..eb70752
--- /dev/null
+++ b/releasenotes/notes/fix-quota-update-zero-args-d596c4169c2d2e30.yaml
@@ -0,0 +1,9 @@
+---
+fixes:
+ - |
+ Fix CLI quota-update to return an error message for no args
+
+ * ``quota-update`` CLI will return an error message
+ ``Must specify a valid resource with new quota value`` if no
+ argument is provided while executing it. If arguments are
+ provided with CLI, no existing behavior is changed.
diff --git a/requirements.txt b/requirements.txt
index 1f8e59e..709fa2e 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -10,7 +10,7 @@ osc-lib>=1.0.2 # Apache-2.0
oslo.i18n>=2.1.0 # Apache-2.0
oslo.serialization>=1.10.0 # Apache-2.0
oslo.utils>=3.16.0 # Apache-2.0
-os-client-config!=1.19.0,!=1.19.1,!=1.20.0,!=1.20.1,>=1.13.1 # Apache-2.0
+os-client-config!=1.19.0,!=1.19.1,!=1.20.0,!=1.20.1,!=1.21.0,>=1.13.1 # Apache-2.0
keystoneauth1>=2.10.0 # Apache-2.0
requests>=2.10.0 # Apache-2.0
simplejson>=2.2.0 # MIT
diff --git a/tools/tox_install.sh b/tools/tox_install.sh
index c800a41..e3fb459 100755
--- a/tools/tox_install.sh
+++ b/tools/tox_install.sh
@@ -15,41 +15,41 @@ CONSTRAINTS_FILE=$1
shift
install_cmd="pip install"
-if [ $CONSTRAINTS_FILE != "unconstrained" ]; then
-
- mydir=$(mktemp -dt "$CLIENT_NAME-tox_install-XXXXXXX")
- localfile=$mydir/upper-constraints.txt
- if [[ $CONSTRAINTS_FILE != http* ]]; then
- CONSTRAINTS_FILE=file://$CONSTRAINTS_FILE
- fi
- curl $CONSTRAINTS_FILE -k -o $localfile
- install_cmd="$install_cmd -c$localfile"
-
- if [ $requirements_installed -eq 0 ]; then
- echo "ALREADY INSTALLED" > /tmp/tox_install.txt
- echo "Requirements already installed; using existing package"
- elif [ -x "$ZUUL_CLONER" ]; then
- export ZUUL_BRANCH=${ZUUL_BRANCH-$BRANCH}
- echo "ZUUL CLONER" > /tmp/tox_install.txt
- pushd $mydir
- $ZUUL_CLONER --cache-dir \
- /opt/git \
- --branch $BRANCH_NAME \
- git://git.openstack.org \
- openstack/requirements
- cd openstack/requirements
- $install_cmd -e .
- popd
- else
- echo "PIP HARDCODE" > /tmp/tox_install.txt
- if [ -z "$REQUIREMENTS_PIP_LOCATION" ]; then
- REQUIREMENTS_PIP_LOCATION="git+https://git.openstack.org/openstack/requirements@$BRANCH_NAME#egg=requirements"
- fi
- $install_cmd -U -e ${REQUIREMENTS_PIP_LOCATION}
+mydir=$(mktemp -dt "$CLIENT_NAME-tox_install-XXXXXXX")
+trap "rm -rf $mydir" EXIT
+localfile=$mydir/upper-constraints.txt
+if [[ $CONSTRAINTS_FILE != http* ]]; then
+ CONSTRAINTS_FILE=file://$CONSTRAINTS_FILE
+fi
+curl $CONSTRAINTS_FILE -k -o $localfile
+install_cmd="$install_cmd -c$localfile"
+
+if [ $requirements_installed -eq 0 ]; then
+ echo "ALREADY INSTALLED" > /tmp/tox_install.txt
+ echo "Requirements already installed; using existing package"
+elif [ -x "$ZUUL_CLONER" ]; then
+ echo "ZUUL CLONER" > /tmp/tox_install.txt
+ pushd $mydir
+ $ZUUL_CLONER --cache-dir \
+ /opt/git \
+ --branch $BRANCH_NAME \
+ git://git.openstack.org \
+ openstack/requirements
+ cd openstack/requirements
+ $install_cmd -e .
+ popd
+else
+ echo "PIP HARDCODE" > /tmp/tox_install.txt
+ if [ -z "$REQUIREMENTS_PIP_LOCATION" ]; then
+ REQUIREMENTS_PIP_LOCATION="git+https://git.openstack.org/openstack/requirements@$BRANCH_NAME#egg=requirements"
fi
-
- edit-constraints $localfile -- $CLIENT_NAME "-e file://$PWD#egg=$CLIENT_NAME"
+ $install_cmd -U -e ${REQUIREMENTS_PIP_LOCATION}
fi
+# This is the main purpose of the script: Allow local installation of
+# the current repo. It is listed in constraints file and thus any
+# install will be constrained and we need to unconstrain it.
+edit-constraints $localfile -- $CLIENT_NAME "-e file://$PWD#egg=$CLIENT_NAME"
+
$install_cmd -U $*
exit $?