Jens Harbott (frickler) a04172969a Revert "Format location columns in network commands"
This reverts commit 6ee7b8d138e07bfc37c5cd887f7afa49cdabb02f.

Change-Id: I5f59959ba8a01aba49e29f4cb007397467344e58
2020-06-17 10:09:36 +00:00

456 lines
16 KiB
Python

# Copyright (c) 2019, 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.
#
"""Network segment action implementations"""
import itertools
import logging
from osc_lib.command import command
from osc_lib import exceptions
from osc_lib import utils
from openstackclient.i18n import _
from openstackclient.identity import common as identity_common
from openstackclient.network import sdk_utils
LOG = logging.getLogger(__name__)
def _get_columns(item):
return sdk_utils.get_osc_show_columns_for_sdk_resource(item, {})
def _get_ranges(item):
item = sorted([int(i) for i in item])
for a, b in itertools.groupby(enumerate(item), lambda xy: xy[1] - xy[0]):
b = list(b)
yield "%s-%s" % (b[0][1], b[-1][1]) if b[0][1] != b[-1][1] else \
str(b[0][1])
def _hack_tuple_value_update_by_index(tup, index, value):
lot = list(tup)
lot[index] = value
return tuple(lot)
def _is_prop_empty(columns, props, prop_name):
return True if not props[columns.index(prop_name)] else False
def _exchange_dict_keys_with_values(orig_dict):
updated_dict = dict()
for k, v in orig_dict.items():
k = [k]
if not updated_dict.get(v):
updated_dict[v] = k
else:
updated_dict[v].extend(k)
return updated_dict
def _update_available_from_props(columns, props):
index_available = columns.index('available')
props = _hack_tuple_value_update_by_index(
props, index_available, list(_get_ranges(props[index_available])))
return props
def _update_used_from_props(columns, props):
index_used = columns.index('used')
updated_used = _exchange_dict_keys_with_values(props[index_used])
for k, v in updated_used.items():
updated_used[k] = list(_get_ranges(v))
props = _hack_tuple_value_update_by_index(
props, index_used, updated_used)
return props
def _update_additional_fields_from_props(columns, props):
props = _update_available_from_props(columns, props)
props = _update_used_from_props(columns, props)
return props
class CreateNetworkSegmentRange(command.ShowOne):
_description = _("Create new network segment range")
def get_parser(self, prog_name):
parser = super(CreateNetworkSegmentRange, self).get_parser(prog_name)
shared_group = parser.add_mutually_exclusive_group()
shared_group.add_argument(
"--private",
dest="private",
action="store_true",
help=_('Network segment range is assigned specifically to the '
'project'),
)
shared_group.add_argument(
"--shared",
dest="shared",
action="store_true",
help=_('Network segment range is shared with other projects'),
)
parser.add_argument(
'name',
metavar='<name>',
help=_('Name of new network segment range')
)
parser.add_argument(
'--project',
metavar='<project>',
help=_('Network segment range owner (name or ID). Optional when '
'the segment range is shared'),
)
identity_common.add_project_domain_option_to_parser(parser)
parser.add_argument(
'--network-type',
metavar='<network-type>',
choices=['geneve', 'gre', 'vlan', 'vxlan'],
required=True,
help=_('Network type of this network segment range '
'(geneve, gre, vlan or vxlan)'),
)
parser.add_argument(
'--physical-network',
metavar='<physical-network-name>',
help=_('Physical network name of this network segment range'),
)
parser.add_argument(
'--minimum',
metavar='<minimum-segmentation-id>',
type=int,
required=True,
help=_('Minimum segment identifier for this network segment '
'range which is based on the network type, VLAN ID for '
'vlan network type and tunnel ID for geneve, gre and vxlan '
'network types'),
)
parser.add_argument(
'--maximum',
metavar='<maximum-segmentation-id>',
type=int,
required=True,
help=_('Maximum segment identifier for this network segment '
'range which is based on the network type, VLAN ID for '
'vlan network type and tunnel ID for geneve, gre and vxlan '
'network types'),
)
return parser
def take_action(self, parsed_args):
network_client = self.app.client_manager.network
try:
# Verify that the extension exists.
network_client.find_extension('network-segment-range',
ignore_missing=False)
except Exception as e:
msg = (_('Network segment range create not supported by '
'Network API: %(e)s') % {'e': e})
raise exceptions.CommandError(msg)
identity_client = self.app.client_manager.identity
if not parsed_args.private and parsed_args.project:
msg = _("--project is only allowed with --private")
raise exceptions.CommandError(msg)
if (parsed_args.network_type.lower() != 'vlan' and
parsed_args.physical_network):
msg = _("--physical-network is only allowed with --network-type "
"vlan")
raise exceptions.CommandError(msg)
attrs = {}
if parsed_args.shared or parsed_args.private:
attrs['shared'] = parsed_args.shared
else:
# default to be ``shared`` if not specified
attrs['shared'] = True
attrs['network_type'] = parsed_args.network_type
attrs['minimum'] = parsed_args.minimum
attrs['maximum'] = parsed_args.maximum
if parsed_args.name:
attrs['name'] = parsed_args.name
if parsed_args.project:
project_id = identity_common.find_project(
identity_client,
parsed_args.project,
parsed_args.project_domain,
).id
if project_id:
attrs['project_id'] = project_id
else:
msg = (_("Failed to create the network segment range for "
"project %(project_id)s") % parsed_args.project_id)
raise exceptions.CommandError(msg)
elif not attrs['shared']:
# default to the current project if no project specified and shared
# is not specified.
# Get the project id from the current auth.
attrs['project_id'] = self.app.client_manager.auth_ref.project_id
if parsed_args.physical_network:
attrs['physical_network'] = parsed_args.physical_network
obj = network_client.create_network_segment_range(**attrs)
display_columns, columns = _get_columns(obj)
data = utils.get_item_properties(obj, columns)
data = _update_additional_fields_from_props(columns, props=data)
return (display_columns, data)
class DeleteNetworkSegmentRange(command.Command):
_description = _("Delete network segment range(s)")
def get_parser(self, prog_name):
parser = super(DeleteNetworkSegmentRange, self).get_parser(prog_name)
parser.add_argument(
'network_segment_range',
metavar='<network-segment-range>',
nargs='+',
help=_('Network segment range(s) to delete (name or ID)'),
)
return parser
def take_action(self, parsed_args):
network_client = self.app.client_manager.network
try:
# Verify that the extension exists.
network_client.find_extension('network-segment-range',
ignore_missing=False)
except Exception as e:
msg = (_('Network segment range delete not supported by '
'Network API: %(e)s') % {'e': e})
raise exceptions.CommandError(msg)
result = 0
for network_segment_range in parsed_args.network_segment_range:
try:
obj = network_client.find_network_segment_range(
network_segment_range, ignore_missing=False)
network_client.delete_network_segment_range(obj)
except Exception as e:
result += 1
LOG.error(_("Failed to delete network segment range with "
"ID '%(network_segment_range)s': %(e)s"),
{'network_segment_range': network_segment_range,
'e': e})
if result > 0:
total = len(parsed_args.network_segment_range)
msg = (_("%(result)s of %(total)s network segment ranges failed "
"to delete.") % {'result': result, 'total': total})
raise exceptions.CommandError(msg)
class ListNetworkSegmentRange(command.Lister):
_description = _("List network segment ranges")
def get_parser(self, prog_name):
parser = super(ListNetworkSegmentRange, self).get_parser(prog_name)
parser.add_argument(
'--long',
action='store_true',
help=_('List additional fields in output'),
)
used_group = parser.add_mutually_exclusive_group()
used_group.add_argument(
'--used',
action='store_true',
help=_('List network segment ranges that have segments in use'),
)
used_group.add_argument(
'--unused',
action='store_true',
help=_('List network segment ranges that have segments '
'not in use'),
)
available_group = parser.add_mutually_exclusive_group()
available_group.add_argument(
'--available',
action='store_true',
help=_('List network segment ranges that have available segments'),
)
available_group.add_argument(
'--unavailable',
action='store_true',
help=_('List network segment ranges without available segments'),
)
return parser
def take_action(self, parsed_args):
network_client = self.app.client_manager.network
try:
# Verify that the extension exists.
network_client.find_extension('network-segment-range',
ignore_missing=False)
except Exception as e:
msg = (_('Network segment ranges list not supported by '
'Network API: %(e)s') % {'e': e})
raise exceptions.CommandError(msg)
filters = {}
data = network_client.network_segment_ranges(**filters)
headers = (
'ID',
'Name',
'Default',
'Shared',
'Project ID',
'Network Type',
'Physical Network',
'Minimum ID',
'Maximum ID'
)
columns = (
'id',
'name',
'default',
'shared',
'project_id',
'network_type',
'physical_network',
'minimum',
'maximum',
)
if parsed_args.available or parsed_args.unavailable or \
parsed_args.used or parsed_args.unused:
# If one of `--available`, `--unavailable`, `--used`,
# `--unused` is specified, we assume that additional fields
# should be listed in output.
parsed_args.long = True
if parsed_args.long:
headers = headers + (
'Used',
'Available',
)
columns = columns + (
'used',
'available',
)
display_props = tuple()
for s in data:
props = utils.get_item_properties(s, columns)
if parsed_args.available and \
_is_prop_empty(columns, props, 'available') or \
parsed_args.unavailable and \
not _is_prop_empty(columns, props, 'available') or \
parsed_args.used and _is_prop_empty(columns, props, 'used') or \
parsed_args.unused and \
not _is_prop_empty(columns, props, 'used'):
continue
if parsed_args.long:
props = _update_additional_fields_from_props(columns, props)
display_props += (props,)
return headers, display_props
class SetNetworkSegmentRange(command.Command):
_description = _("Set network segment range properties")
def get_parser(self, prog_name):
parser = super(SetNetworkSegmentRange, self).get_parser(prog_name)
parser.add_argument(
'network_segment_range',
metavar='<network-segment-range>',
help=_('Network segment range to modify (name or ID)'),
)
parser.add_argument(
'--name',
metavar='<name>',
help=_('Set network segment name'),
)
parser.add_argument(
'--minimum',
metavar='<minimum-segmentation-id>',
type=int,
help=_('Set network segment range minimum segment identifier'),
)
parser.add_argument(
'--maximum',
metavar='<maximum-segmentation-id>',
type=int,
help=_('Set network segment range maximum segment identifier'),
)
return parser
def take_action(self, parsed_args):
network_client = self.app.client_manager.network
try:
# Verify that the extension exists.
network_client.find_extension('network-segment-range',
ignore_missing=False)
except Exception as e:
msg = (_('Network segment range set not supported by '
'Network API: %(e)s') % {'e': e})
raise exceptions.CommandError(msg)
if (parsed_args.minimum and not parsed_args.maximum) or \
(parsed_args.maximum and not parsed_args.minimum):
msg = _("--minimum and --maximum are both required")
raise exceptions.CommandError(msg)
obj = network_client.find_network_segment_range(
parsed_args.network_segment_range, ignore_missing=False)
attrs = {}
if parsed_args.name:
attrs['name'] = parsed_args.name
if parsed_args.minimum:
attrs['minimum'] = parsed_args.minimum
if parsed_args.maximum:
attrs['maximum'] = parsed_args.maximum
network_client.update_network_segment_range(obj, **attrs)
class ShowNetworkSegmentRange(command.ShowOne):
_description = _("Display network segment range details")
def get_parser(self, prog_name):
parser = super(ShowNetworkSegmentRange, self).get_parser(prog_name)
parser.add_argument(
'network_segment_range',
metavar='<network-segment-range>',
help=_('Network segment range to display (name or ID)'),
)
return parser
def take_action(self, parsed_args):
network_client = self.app.client_manager.network
try:
# Verify that the extension exists.
network_client.find_extension('network-segment-range',
ignore_missing=False)
except Exception as e:
msg = (_('Network segment range show not supported by '
'Network API: %(e)s') % {'e': e})
raise exceptions.CommandError(msg)
obj = network_client.find_network_segment_range(
parsed_args.network_segment_range,
ignore_missing=False
)
display_columns, columns = _get_columns(obj)
data = utils.get_item_properties(obj, columns)
data = _update_additional_fields_from_props(columns, props=data)
return (display_columns, data)