summaryrefslogtreecommitdiff
path: root/openstackclient/network/v2
diff options
context:
space:
mode:
Diffstat (limited to 'openstackclient/network/v2')
-rw-r--r--openstackclient/network/v2/floating_ip.py81
-rw-r--r--openstackclient/network/v2/network_agent.py28
-rw-r--r--openstackclient/network/v2/network_qos_rule.py356
-rw-r--r--openstackclient/network/v2/security_group_rule.py45
4 files changed, 483 insertions, 27 deletions
diff --git a/openstackclient/network/v2/floating_ip.py b/openstackclient/network/v2/floating_ip.py
index 8202b3fa..980c41c7 100644
--- a/openstackclient/network/v2/floating_ip.py
+++ b/openstackclient/network/v2/floating_ip.py
@@ -15,6 +15,8 @@
import logging
+from openstack import exceptions as sdk_exceptions
+from openstack.network.v2 import floating_ip as _floating_ip
from osc_lib import utils
from openstackclient.i18n import _
@@ -79,6 +81,58 @@ def _get_attrs(client_manager, parsed_args):
return attrs
+def _find_floating_ip(
+ session,
+ ip_cache,
+ name_or_id,
+ ignore_missing=True,
+ **params
+):
+ """Find a floating IP by IP or ID
+
+ The SDK's find_ip() can only locate a floating IP by ID so we have
+ to do this ourselves.
+ """
+
+ def _get_one_match(name_or_id):
+ """Given a list of results, return the match"""
+ the_result = None
+ for maybe_result in ip_cache:
+ id_value = maybe_result.id
+ ip_value = maybe_result.floating_ip_address
+
+ if (id_value == name_or_id) or (ip_value == name_or_id):
+ # Only allow one resource to be found. If we already
+ # found a match, raise an exception to show it.
+ if the_result is None:
+ the_result = maybe_result
+ else:
+ msg = "More than one %s exists with the name '%s'."
+ msg = (msg % (_floating_ip.FloatingIP, name_or_id))
+ raise sdk_exceptions.DuplicateResource(msg)
+
+ return the_result
+
+ # Try to short-circuit by looking directly for a matching ID.
+ try:
+ match = _floating_ip.FloatingIP.existing(id=name_or_id, **params)
+ return (match.get(session), ip_cache)
+ except sdk_exceptions.NotFoundException:
+ pass
+
+ if len(ip_cache) == 0:
+ ip_cache = list(_floating_ip.FloatingIP.list(session, **params))
+
+ result = _get_one_match(name_or_id)
+ if result is not None:
+ return (result, ip_cache)
+
+ if ignore_missing:
+ return (None, ip_cache)
+ raise sdk_exceptions.ResourceNotFound(
+ "No %s found for %s" % (_floating_ip.FloatingIP.__name__, name_or_id))
+
+
class CreateFloatingIP(common.NetworkAndComputeShowOne):
_description = _("Create floating IP")
@@ -186,13 +240,28 @@ class DeleteFloatingIP(common.NetworkAndComputeDelete):
return parser
def take_action_network(self, client, parsed_args):
- obj = client.find_ip(self.r, ignore_missing=False)
+ (obj, self.ip_cache) = _find_floating_ip(
+ client.session,
+ self.ip_cache,
+ self.r,
+ ignore_missing=False,
+ )
client.delete_ip(obj)
def take_action_compute(self, client, parsed_args):
obj = utils.find_resource(client.floating_ips, self.r)
client.floating_ips.delete(obj.id)
+ def take_action(self, parsed_args):
+ """Implements a naive cache for the list of floating IPs"""
+
+ # NOTE(dtroyer): This really only prevents multiple list()
+ # calls when performing multiple resource deletes
+ # in a single command. In an interactive session
+ # each delete command will call list().
+ self.ip_cache = []
+ super(DeleteFloatingIP, self).take_action(parsed_args)
+
class DeleteIPFloating(DeleteFloatingIP):
_description = _("Delete floating IP(s)")
@@ -390,6 +459,9 @@ class ListIPFloating(ListFloatingIP):
class ShowFloatingIP(common.NetworkAndComputeShowOne):
_description = _("Display floating IP details")
+ # ip_cache is unused here but is a side effect of _find_floating_ip()
+ ip_cache = []
+
def update_parser_common(self, parser):
parser.add_argument(
'floating_ip',
@@ -399,7 +471,12 @@ class ShowFloatingIP(common.NetworkAndComputeShowOne):
return parser
def take_action_network(self, client, parsed_args):
- obj = client.find_ip(parsed_args.floating_ip, ignore_missing=False)
+ (obj, self.ip_cache) = _find_floating_ip(
+ client.session,
+ [],
+ parsed_args.floating_ip,
+ ignore_missing=False,
+ )
display_columns, columns = _get_network_columns(obj)
data = utils.get_item_properties(obj, columns)
return (display_columns, data)
diff --git a/openstackclient/network/v2/network_agent.py b/openstackclient/network/v2/network_agent.py
index b3411166..d429fa08 100644
--- a/openstackclient/network/v2/network_agent.py
+++ b/openstackclient/network/v2/network_agent.py
@@ -20,6 +20,7 @@ from osc_lib import exceptions
from osc_lib import utils
from openstackclient.i18n import _
+from openstackclient.network import sdk_utils
LOG = logging.getLogger(__name__)
@@ -31,10 +32,19 @@ def _format_admin_state(state):
_formatters = {
'admin_state_up': _format_admin_state,
+ 'is_admin_state_up': _format_admin_state,
'configurations': utils.format_dict,
}
+def _get_network_columns(item):
+ column_map = {
+ 'is_admin_state_up': 'admin_state_up',
+ 'is_alive': 'alive',
+ }
+ return sdk_utils.get_osc_show_columns_for_sdk_resource(item, column_map)
+
+
class DeleteNetworkAgent(command.Command):
_description = _("Delete network agent(s)")
@@ -69,6 +79,8 @@ class DeleteNetworkAgent(command.Command):
raise exceptions.CommandError(msg)
+# TODO(huanxuan): Use the SDK resource mapped attribute names once the
+# OSC minimum requirements include SDK 1.0.
class ListNetworkAgent(command.Lister):
_description = _("List network agents")
@@ -98,8 +110,8 @@ class ListNetworkAgent(command.Lister):
'agent_type',
'host',
'availability_zone',
- 'alive',
- 'admin_state_up',
+ 'is_alive',
+ 'is_admin_state_up',
'binary'
)
column_headers = (
@@ -138,6 +150,8 @@ class ListNetworkAgent(command.Lister):
) for s in data))
+# TODO(huanxuan): Use the SDK resource mapped attribute names once the
+# OSC minimum requirements include SDK 1.0.
class SetNetworkAgent(command.Command):
_description = _("Set network agent properties")
@@ -168,10 +182,12 @@ class SetNetworkAgent(command.Command):
def take_action(self, parsed_args):
client = self.app.client_manager.network
- obj = client.get_agent(parsed_args.network_agent, ignore_missing=False)
+ obj = client.get_agent(parsed_args.network_agent)
attrs = {}
if parsed_args.description is not None:
attrs['description'] = str(parsed_args.description)
+ # TODO(huanxuan): Also update by the new attribute name
+ # "is_admin_state_up" after sdk 0.9.12
if parsed_args.enable:
attrs['admin_state_up'] = True
if parsed_args.disable:
@@ -193,7 +209,7 @@ class ShowNetworkAgent(command.ShowOne):
def take_action(self, parsed_args):
client = self.app.client_manager.network
- obj = client.get_agent(parsed_args.network_agent, ignore_missing=False)
- columns = tuple(sorted(list(obj.keys())))
+ obj = client.get_agent(parsed_args.network_agent)
+ display_columns, columns = _get_network_columns(obj)
data = utils.get_item_properties(obj, columns, formatters=_formatters,)
- return columns, data
+ return display_columns, data
diff --git a/openstackclient/network/v2/network_qos_rule.py b/openstackclient/network/v2/network_qos_rule.py
new file mode 100644
index 00000000..a662ca18
--- /dev/null
+++ b/openstackclient/network/v2/network_qos_rule.py
@@ -0,0 +1,356 @@
+# Copyright (c) 2016, Intel Corporation.
+# All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+import itertools
+import logging
+import six
+
+from osc_lib.command import command
+from osc_lib import exceptions
+from osc_lib import utils
+
+from openstackclient.i18n import _
+from openstackclient.network import sdk_utils
+
+
+LOG = logging.getLogger(__name__)
+
+RULE_TYPE_BANDWIDTH_LIMIT = 'bandwidth-limit'
+RULE_TYPE_DSCP_MARKING = 'dscp-marking'
+RULE_TYPE_MINIMUM_BANDWIDTH = 'minimum-bandwidth'
+REQUIRED_PARAMETERS = {
+ RULE_TYPE_MINIMUM_BANDWIDTH: ['min_kbps', 'direction'],
+ RULE_TYPE_DSCP_MARKING: ['dscp_mark'],
+ RULE_TYPE_BANDWIDTH_LIMIT: ['max_kbps', 'max_burst_kbps']}
+DIRECTION_EGRESS = 'egress'
+DIRECTION_INGRESS = 'ingress'
+DSCP_VALID_MARKS = [0, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30, 32,
+ 34, 36, 38, 40, 46, 48, 56]
+
+ACTION_CREATE = 'create'
+ACTION_DELETE = 'delete'
+ACTION_FIND = 'find'
+ACTION_SET = 'update'
+ACTION_SHOW = 'get'
+
+
+def _get_columns(item):
+ column_map = {
+ 'tenant_id': 'project_id',
+ }
+ return sdk_utils.get_osc_show_columns_for_sdk_resource(item, column_map)
+
+
+def _check_type_parameters(attrs, type, is_create):
+ req_params = REQUIRED_PARAMETERS[type]
+ notreq_params = list(itertools.chain(
+ *[v for k, v in six.iteritems(REQUIRED_PARAMETERS) if k != type]))
+ if is_create and None in map(attrs.get, req_params):
+ msg = (_('"Create" rule command for type "%(rule_type)s" requires '
+ 'arguments %(args)s') % {'rule_type': type,
+ 'args': ", ".join(req_params)})
+ raise exceptions.CommandError(msg)
+ if set(six.iterkeys(attrs)) & set(notreq_params):
+ msg = (_('Rule type "%(rule_type)s" only requires arguments %(args)s')
+ % {'rule_type': type, 'args': ", ".join(req_params)})
+ raise exceptions.CommandError(msg)
+
+
+def _get_attrs(network_client, parsed_args, is_create=False):
+ attrs = {}
+ qos = network_client.find_qos_policy(parsed_args.qos_policy,
+ ignore_missing=False)
+ attrs['qos_policy_id'] = qos.id
+ if not is_create:
+ attrs['id'] = parsed_args.id
+ rule_type = _find_rule_type(qos, parsed_args.id)
+ if not rule_type:
+ msg = (_('Rule ID %(rule_id)s not found') %
+ {'rule_id': parsed_args.id})
+ raise exceptions.CommandError(msg)
+ else:
+ if not parsed_args.type:
+ msg = _('"Create" rule command requires argument "type"')
+ raise exceptions.CommandError(msg)
+ rule_type = parsed_args.type
+ if parsed_args.max_kbps is not None:
+ attrs['max_kbps'] = parsed_args.max_kbps
+ if parsed_args.max_burst_kbits is not None:
+ # NOTE(ralonsoh): this parameter must be changed in SDK and then in
+ # Neutron API, from 'max_burst_kbps' to
+ # 'max_burst_kbits'
+ attrs['max_burst_kbps'] = parsed_args.max_burst_kbits
+ if parsed_args.dscp_mark is not None:
+ attrs['dscp_mark'] = parsed_args.dscp_mark
+ if parsed_args.min_kbps is not None:
+ attrs['min_kbps'] = parsed_args.min_kbps
+ if parsed_args.ingress:
+ attrs['direction'] = 'ingress'
+ if parsed_args.egress:
+ attrs['direction'] = 'egress'
+ _check_type_parameters(attrs, rule_type, is_create)
+ return attrs
+
+
+def _get_item_properties(item, fields):
+ """Return a tuple containing the item properties."""
+ row = []
+ for field in fields:
+ row.append(item.get(field, ''))
+ return tuple(row)
+
+
+def _rule_action_call(client, action, rule_type):
+ rule_type = rule_type.replace('-', '_')
+ func_name = '%(action)s_qos_%(rule_type)s_rule' % {'action': action,
+ 'rule_type': rule_type}
+ return getattr(client, func_name)
+
+
+def _find_rule_type(qos, rule_id):
+ for rule in (r for r in qos.rules if r['id'] == rule_id):
+ return rule['type'].replace('_', '-')
+ return None
+
+
+def _add_rule_arguments(parser):
+ parser.add_argument(
+ '--max-kbps',
+ dest='max_kbps',
+ metavar='<max-kbps>',
+ type=int,
+ help=_('Maximum bandwidth in kbps')
+ )
+ parser.add_argument(
+ '--max-burst-kbits',
+ dest='max_burst_kbits',
+ metavar='<max-burst-kbits>',
+ type=int,
+ help=_('Maximum burst in kilobits, 0 means automatic')
+ )
+ parser.add_argument(
+ '--dscp-mark',
+ dest='dscp_mark',
+ metavar='<dscp-mark>',
+ type=int,
+ help=_('DSCP mark: value can be 0, even numbers from 8-56, '
+ 'excluding 42, 44, 50, 52, and 54')
+ )
+ parser.add_argument(
+ '--min-kbps',
+ dest='min_kbps',
+ metavar='<min-kbps>',
+ type=int,
+ help=_('Minimum guaranteed bandwidth in kbps')
+ )
+ direction_group = parser.add_mutually_exclusive_group()
+ direction_group.add_argument(
+ '--ingress',
+ action='store_true',
+ help=_("Ingress traffic direction from the project point of view")
+ )
+ direction_group.add_argument(
+ '--egress',
+ action='store_true',
+ help=_("Egress traffic direction from the project point of view")
+ )
+
+
+class CreateNetworkQosRule(command.ShowOne):
+ _description = _("Create new Network QoS rule")
+
+ def get_parser(self, prog_name):
+ parser = super(CreateNetworkQosRule, self).get_parser(
+ prog_name)
+ parser.add_argument(
+ 'qos_policy',
+ metavar='<qos-policy>',
+ help=_('QoS policy that contains the rule (name or ID)')
+ )
+ parser.add_argument(
+ '--type',
+ metavar='<type>',
+ choices=[RULE_TYPE_MINIMUM_BANDWIDTH,
+ RULE_TYPE_DSCP_MARKING,
+ RULE_TYPE_BANDWIDTH_LIMIT],
+ help=(_('QoS rule type (%s)') %
+ ", ".join(six.iterkeys(REQUIRED_PARAMETERS)))
+ )
+ _add_rule_arguments(parser)
+ return parser
+
+ def take_action(self, parsed_args):
+ network_client = self.app.client_manager.network
+ attrs = _get_attrs(network_client, parsed_args, is_create=True)
+ try:
+ obj = _rule_action_call(
+ network_client, ACTION_CREATE, parsed_args.type)(
+ attrs.pop('qos_policy_id'), **attrs)
+ except Exception as e:
+ msg = (_('Failed to create Network QoS rule: %(e)s') % {'e': e})
+ raise exceptions.CommandError(msg)
+ display_columns, columns = _get_columns(obj)
+ data = utils.get_item_properties(obj, columns)
+ return display_columns, data
+
+
+class DeleteNetworkQosRule(command.Command):
+ _description = _("Delete Network QoS rule")
+
+ def get_parser(self, prog_name):
+ parser = super(DeleteNetworkQosRule, self).get_parser(prog_name)
+ parser.add_argument(
+ 'qos_policy',
+ metavar='<qos-policy>',
+ help=_('QoS policy that contains the rule (name or ID)')
+ )
+ parser.add_argument(
+ 'id',
+ metavar='<rule-id>',
+ help=_('Network QoS rule to delete (ID)')
+ )
+ return parser
+
+ def take_action(self, parsed_args):
+ network_client = self.app.client_manager.network
+ rule_id = parsed_args.id
+ try:
+ qos = network_client.find_qos_policy(parsed_args.qos_policy,
+ ignore_missing=False)
+ rule_type = _find_rule_type(qos, rule_id)
+ if not rule_type:
+ raise Exception('Rule %s not found' % rule_id)
+ _rule_action_call(network_client, ACTION_DELETE, rule_type)(
+ rule_id, qos.id)
+ except Exception as e:
+ msg = (_('Failed to delete Network QoS rule ID "%(rule)s": %(e)s')
+ % {'rule': rule_id, 'e': e})
+ raise exceptions.CommandError(msg)
+
+
+class ListNetworkQosRule(command.Lister):
+ _description = _("List Network QoS rules")
+
+ def get_parser(self, prog_name):
+ parser = super(ListNetworkQosRule, self).get_parser(prog_name)
+ parser.add_argument(
+ 'qos_policy',
+ metavar='<qos-policy>',
+ help=_('QoS policy that contains the rule (name or ID)')
+ )
+ return parser
+
+ def take_action(self, parsed_args):
+ client = self.app.client_manager.network
+ columns = (
+ 'id',
+ 'qos_policy_id',
+ 'type',
+ 'max_kbps',
+ 'max_burst_kbps',
+ 'min_kbps',
+ 'dscp_mark',
+ 'direction',
+ )
+ column_headers = (
+ 'ID',
+ 'QoS Policy ID',
+ 'Type',
+ 'Max Kbps',
+ 'Max Burst Kbits',
+ 'Min Kbps',
+ 'DSCP mark',
+ 'Direction',
+ )
+ qos = client.find_qos_policy(parsed_args.qos_policy,
+ ignore_missing=False)
+ data = qos.rules
+ return (column_headers,
+ (_get_item_properties(s, columns) for s in data))
+
+
+class SetNetworkQosRule(command.Command):
+ _description = _("Set Network QoS rule properties")
+
+ def get_parser(self, prog_name):
+ parser = super(SetNetworkQosRule, self).get_parser(prog_name)
+ parser.add_argument(
+ 'qos_policy',
+ metavar='<qos-policy>',
+ help=_('QoS policy that contains the rule (name or ID)')
+ )
+ parser.add_argument(
+ 'id',
+ metavar='<rule-id>',
+ help=_('Network QoS rule to delete (ID)')
+ )
+ _add_rule_arguments(parser)
+ return parser
+
+ def take_action(self, parsed_args):
+ network_client = self.app.client_manager.network
+ try:
+ qos = network_client.find_qos_policy(parsed_args.qos_policy,
+ ignore_missing=False)
+ rule_type = _find_rule_type(qos, parsed_args.id)
+ if not rule_type:
+ raise Exception('Rule not found')
+ attrs = _get_attrs(network_client, parsed_args)
+ qos_id = attrs.pop('qos_policy_id')
+ qos_rule = _rule_action_call(network_client, ACTION_FIND,
+ rule_type)(attrs.pop('id'), qos_id)
+ _rule_action_call(network_client, ACTION_SET, rule_type)(
+ qos_rule, qos_id, **attrs)
+ except Exception as e:
+ msg = (_('Failed to set Network QoS rule ID "%(rule)s": %(e)s') %
+ {'rule': parsed_args.id, 'e': e})
+ raise exceptions.CommandError(msg)
+
+
+class ShowNetworkQosRule(command.ShowOne):
+ _description = _("Display Network QoS rule details")
+
+ def get_parser(self, prog_name):
+ parser = super(ShowNetworkQosRule, self).get_parser(prog_name)
+ parser.add_argument(
+ 'qos_policy',
+ metavar='<qos-policy>',
+ help=_('QoS policy that contains the rule (name or ID)')
+ )
+ parser.add_argument(
+ 'id',
+ metavar='<rule-id>',
+ help=_('Network QoS rule to delete (ID)')
+ )
+ return parser
+
+ def take_action(self, parsed_args):
+ network_client = self.app.client_manager.network
+ rule_id = parsed_args.id
+ try:
+ qos = network_client.find_qos_policy(parsed_args.qos_policy,
+ ignore_missing=False)
+ rule_type = _find_rule_type(qos, rule_id)
+ if not rule_type:
+ raise Exception('Rule not found')
+ obj = _rule_action_call(network_client, ACTION_SHOW, rule_type)(
+ rule_id, qos.id)
+ except Exception as e:
+ msg = (_('Failed to set Network QoS rule ID "%(rule)s": %(e)s') %
+ {'rule': rule_id, 'e': e})
+ raise exceptions.CommandError(msg)
+ display_columns, columns = _get_columns(obj)
+ data = utils.get_item_properties(obj, columns)
+ return display_columns, data
diff --git a/openstackclient/network/v2/security_group_rule.py b/openstackclient/network/v2/security_group_rule.py
index b878d875..4fb62c7b 100644
--- a/openstackclient/network/v2/security_group_rule.py
+++ b/openstackclient/network/v2/security_group_rule.py
@@ -51,17 +51,17 @@ def _format_network_port_range(rule):
# - Single port: '80:80'
# - No port range: ''
port_range = ''
- if _is_icmp_protocol(rule.protocol):
- if rule.port_range_min:
- port_range += 'type=' + str(rule.port_range_min)
- if rule.port_range_max:
- port_range += ':code=' + str(rule.port_range_max)
- elif rule.port_range_min or rule.port_range_max:
- port_range_min = str(rule.port_range_min)
- port_range_max = str(rule.port_range_max)
- if rule.port_range_min is None:
+ if _is_icmp_protocol(rule['protocol']):
+ if rule['port_range_min']:
+ port_range += 'type=' + str(rule['port_range_min'])
+ if rule['port_range_max']:
+ port_range += ':code=' + str(rule['port_range_max'])
+ elif rule['port_range_min'] or rule['port_range_max']:
+ port_range_min = str(rule['port_range_min'])
+ port_range_max = str(rule['port_range_max'])
+ if rule['port_range_min'] is None:
port_range_min = port_range_max
- if rule.port_range_max is None:
+ if rule['port_range_max'] is None:
port_range_max = port_range_min
port_range = port_range_min + ':' + port_range_max
return port_range
@@ -423,6 +423,16 @@ class DeleteSecurityGroupRule(common.NetworkAndComputeDelete):
class ListSecurityGroupRule(common.NetworkAndComputeLister):
_description = _("List security group rules")
+ def _format_network_security_group_rule(self, rule):
+ """Transform the SDK SecurityGroupRule object to a dict
+
+ The SDK object gets in the way of reformatting columns...
+ Create port_range column from port_range_min and port_range_max
+ """
+ rule = rule.to_dict()
+ rule['port_range'] = _format_network_port_range(rule)
+ return rule
+
def update_parser_common(self, parser):
parser.add_argument(
'group',
@@ -508,7 +518,7 @@ class ListSecurityGroupRule(common.NetworkAndComputeLister):
'id',
'protocol',
'remote_ip_prefix',
- 'port_range_min',
+ 'port_range',
)
if parsed_args.long:
columns = columns + ('direction', 'ethertype',)
@@ -535,16 +545,13 @@ class ListSecurityGroupRule(common.NetworkAndComputeLister):
if parsed_args.protocol is not None:
query['protocol'] = parsed_args.protocol
- rules = list(client.security_group_rules(**query))
-
- # Reformat the rules to display a port range instead
- # of just the port range minimum. This maintains
- # output compatibility with compute.
- for rule in rules:
- rule.port_range_min = _format_network_port_range(rule)
+ rules = [
+ self._format_network_security_group_rule(r)
+ for r in client.security_group_rules(**query)
+ ]
return (column_headers,
- (utils.get_item_properties(
+ (utils.get_dict_properties(
s, columns,
) for s in rules))