summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--doc/source/cli/command-objects/local-ip-association.rst11
-rw-r--r--doc/source/cli/command-objects/local-ip.rst12
-rw-r--r--doc/source/cli/command-objects/server-migration.rst2
-rw-r--r--doc/source/cli/data/nova.csv2
-rw-r--r--doc/source/cli/plugin-commands/aodh.rst4
-rw-r--r--doc/source/cli/plugin-commands/index.rst5
-rw-r--r--lower-constraints.txt2
-rw-r--r--openstackclient/common/quota.py9
-rw-r--r--openstackclient/compute/v2/aggregate.py2
-rw-r--r--openstackclient/compute/v2/flavor.py2
-rw-r--r--openstackclient/compute/v2/server.py508
-rw-r--r--openstackclient/compute/v2/server_image.py18
-rw-r--r--openstackclient/compute/v2/service.py131
-rw-r--r--openstackclient/network/v2/address_group.py13
-rw-r--r--openstackclient/network/v2/address_scope.py11
-rw-r--r--openstackclient/network/v2/floating_ip.py17
-rw-r--r--openstackclient/network/v2/floating_ip_port_forwarding.py11
-rw-r--r--openstackclient/network/v2/ip_availability.py12
-rw-r--r--openstackclient/network/v2/l3_conntrack_helper.py7
-rw-r--r--openstackclient/network/v2/local_ip.py310
-rw-r--r--openstackclient/network/v2/local_ip_association.py197
-rw-r--r--openstackclient/network/v2/network.py15
-rw-r--r--openstackclient/network/v2/network_agent.py7
-rw-r--r--openstackclient/network/v2/network_auto_allocated_topology.py13
-rw-r--r--openstackclient/network/v2/network_flavor.py10
-rw-r--r--openstackclient/network/v2/network_flavor_profile.py10
-rw-r--r--openstackclient/network/v2/network_meter.py10
-rw-r--r--openstackclient/network/v2/network_meter_rule.py13
-rw-r--r--openstackclient/network/v2/network_qos_policy.py10
-rw-r--r--openstackclient/network/v2/network_qos_rule.py11
-rw-r--r--openstackclient/network/v2/network_qos_rule_type.py2
-rw-r--r--openstackclient/network/v2/network_rbac.py10
-rw-r--r--openstackclient/network/v2/network_segment.py8
-rw-r--r--openstackclient/network/v2/network_segment_range.py8
-rw-r--r--openstackclient/network/v2/port.py28
-rw-r--r--openstackclient/network/v2/router.py85
-rw-r--r--openstackclient/network/v2/security_group.py12
-rw-r--r--openstackclient/network/v2/security_group_rule.py13
-rw-r--r--openstackclient/network/v2/subnet.py22
-rw-r--r--openstackclient/network/v2/subnet_pool.py11
-rw-r--r--openstackclient/tests/functional/common/test_quota.py25
-rw-r--r--openstackclient/tests/functional/compute/v2/test_server.py149
-rw-r--r--openstackclient/tests/functional/network/v2/test_local_ip.py161
-rw-r--r--openstackclient/tests/functional/volume/v3/test_volume.py1
-rw-r--r--openstackclient/tests/unit/common/test_quota.py43
-rw-r--r--openstackclient/tests/unit/compute/v2/fakes.py96
-rw-r--r--openstackclient/tests/unit/compute/v2/test_aggregate.py2
-rw-r--r--openstackclient/tests/unit/compute/v2/test_server.py1346
-rw-r--r--openstackclient/tests/unit/compute/v2/test_server_image.py27
-rw-r--r--openstackclient/tests/unit/compute/v2/test_service.py248
-rw-r--r--openstackclient/tests/unit/network/v2/fakes.py255
-rw-r--r--openstackclient/tests/unit/network/v2/test_address_group.py4
-rw-r--r--openstackclient/tests/unit/network/v2/test_address_scope.py8
-rw-r--r--openstackclient/tests/unit/network/v2/test_floating_ip_network.py14
-rw-r--r--openstackclient/tests/unit/network/v2/test_floating_ip_port_forwarding.py6
-rw-r--r--openstackclient/tests/unit/network/v2/test_ip_availability.py5
-rw-r--r--openstackclient/tests/unit/network/v2/test_local_ip.py480
-rw-r--r--openstackclient/tests/unit/network/v2/test_local_ip_association.py328
-rw-r--r--openstackclient/tests/unit/network/v2/test_network.py12
-rw-r--r--openstackclient/tests/unit/network/v2/test_network_auto_allocated_topology.py6
-rw-r--r--openstackclient/tests/unit/network/v2/test_network_flavor.py2
-rw-r--r--openstackclient/tests/unit/network/v2/test_network_flavor_profile.py6
-rw-r--r--openstackclient/tests/unit/network/v2/test_network_meter.py2
-rw-r--r--openstackclient/tests/unit/network/v2/test_network_qos_policy.py6
-rw-r--r--openstackclient/tests/unit/network/v2/test_network_rbac.py14
-rw-r--r--openstackclient/tests/unit/network/v2/test_port.py27
-rw-r--r--openstackclient/tests/unit/network/v2/test_router.py47
-rw-r--r--openstackclient/tests/unit/network/v2/test_security_group_network.py6
-rw-r--r--openstackclient/tests/unit/network/v2/test_security_group_rule_network.py2
-rw-r--r--openstackclient/tests/unit/network/v2/test_subnet.py56
-rw-r--r--openstackclient/tests/unit/network/v2/test_subnet_pool.py8
-rw-r--r--openstackclient/tests/unit/volume/v2/fakes.py110
-rw-r--r--releasenotes/notes/add-network-local-ip-df3a9ce7610d8b90.yaml8
-rw-r--r--releasenotes/notes/add-option-to-unset-port-host-c76de9b1d2addf9a.yaml5
-rw-r--r--releasenotes/notes/check-limit-quota-cc7f291dd1b537c1.yaml5
-rw-r--r--releasenotes/notes/fix-flavor-in-server-list-microversion-2.47-af200e9bb4747e2d.yaml8
-rw-r--r--releasenotes/notes/list-subnet-by-pool-id-a642efc13d04fa08.yaml5
-rw-r--r--releasenotes/notes/migrate-add-fixed-ip-to-sdk-3d932d77633bc765.yaml3
-rw-r--r--releasenotes/notes/migrate-create-server-image-to-sdk-e3d8077ffe05bb3d.yaml4
-rw-r--r--releasenotes/notes/migrate-server-add-volume-to-sdk-685e036a88839651.yaml4
-rw-r--r--releasenotes/notes/migrate-server-pause-unpause-to-sdk-d74ec8536b764af6.yaml5
-rw-r--r--releasenotes/notes/migrate-server-suspend-resume-to-sdk-fd1709336607b496.yaml5
-rw-r--r--releasenotes/notes/migrate-service-list-delete-set-to-sdk-920cbe0d210af565.yaml3
-rw-r--r--releasenotes/notes/options-create-router-97910a882b604652.yaml8
-rw-r--r--releasenotes/notes/pass_ssh_args-cf26a2ce26ccddaf.yaml14
-rw-r--r--releasenotes/notes/port-list-security-group-4af5d2e789174ff9.yaml5
-rw-r--r--releasenotes/notes/switch-server-remove-network-port-to-sdk-829ba711e0e198d5.yaml4
-rw-r--r--releasenotes/notes/switch-server-remove-volume-to-sdk-47e9befd2672dcdf.yaml4
-rw-r--r--releasenotes/source/conf.py8
-rw-r--r--requirements.txt2
-rw-r--r--setup.cfg10
91 files changed, 4055 insertions, 1141 deletions
diff --git a/doc/source/cli/command-objects/local-ip-association.rst b/doc/source/cli/command-objects/local-ip-association.rst
new file mode 100644
index 00000000..824ee4d0
--- /dev/null
+++ b/doc/source/cli/command-objects/local-ip-association.rst
@@ -0,0 +1,11 @@
+=============================================
+Local IP Associations (local_ip_associations)
+=============================================
+
+The resource lets users assign Local IPs to user Ports.
+This is a sub-resource of the Local IP resource.
+
+Network v2
+
+.. autoprogram-cliff:: openstack.network.v2
+ :command: local ip association *
diff --git a/doc/source/cli/command-objects/local-ip.rst b/doc/source/cli/command-objects/local-ip.rst
new file mode 100644
index 00000000..c8c5ab47
--- /dev/null
+++ b/doc/source/cli/command-objects/local-ip.rst
@@ -0,0 +1,12 @@
+=====================
+Local IPs (local_ips)
+=====================
+
+Extension that allows users to create a virtual IP that can later be assigned
+to multiple ports/VMs (similar to anycast IP) and is guaranteed to only be
+reachable within the same physical server/node boundaries
+
+Network v2
+
+.. autoprogram-cliff:: openstack.network.v2
+ :command: local ip *
diff --git a/doc/source/cli/command-objects/server-migration.rst b/doc/source/cli/command-objects/server-migration.rst
index 6e2982cf..9db58e3e 100644
--- a/doc/source/cli/command-objects/server-migration.rst
+++ b/doc/source/cli/command-objects/server-migration.rst
@@ -9,4 +9,4 @@ supported: live migration, cold migration, resize and evacuation.
Compute v2
.. autoprogram-cliff:: openstack.compute.v2
- :command: server migration list
+ :command: server migration *
diff --git a/doc/source/cli/data/nova.csv b/doc/source/cli/data/nova.csv
index dd8992f6..0ab2b2fe 100644
--- a/doc/source/cli/data/nova.csv
+++ b/doc/source/cli/data/nova.csv
@@ -46,7 +46,7 @@ hypervisor-show,hypervisor show,Display the details of the specified hypervisor.
hypervisor-stats,hypervisor stats show,Get hypervisor statistics over all compute nodes.
hypervisor-uptime,,Display the uptime of the specified hypervisor.
image-create,server image create,Create a new image by taking a snapshot of a running server.
-instance-action,,Show an action.
+instance-action,server event show,Show an action.
instance-action-list,,List actions on a server.
instance-usage-audit-log,,List/Get server usage audits.
interface-attach,server add port / server add floating ip / server add fixed ip,Attach a network interface to a server.
diff --git a/doc/source/cli/plugin-commands/aodh.rst b/doc/source/cli/plugin-commands/aodh.rst
deleted file mode 100644
index 5d8b4332..00000000
--- a/doc/source/cli/plugin-commands/aodh.rst
+++ /dev/null
@@ -1,4 +0,0 @@
-aodh
-----
-
-.. autoprogram-cliff:: openstack.alarming.v2
diff --git a/doc/source/cli/plugin-commands/index.rst b/doc/source/cli/plugin-commands/index.rst
index 4e1ce54b..638dcbe5 100644
--- a/doc/source/cli/plugin-commands/index.rst
+++ b/doc/source/cli/plugin-commands/index.rst
@@ -7,7 +7,6 @@ Plugin Commands
.. toctree::
:maxdepth: 1
- aodh
barbican
designate
gnocchi
@@ -29,6 +28,10 @@ Plugin Commands
.. TODO(efried): Make pages for the following once they're fixed.
+.. aodh
+.. # aodhclient docs build is failing with recent pyparsing
+.. # autoprogram-cliff:: openstack.alarming.v2
+
.. cue
.. # cueclient is not in global-requirements
.. # list-plugins:: openstack.mb.v1
diff --git a/lower-constraints.txt b/lower-constraints.txt
index b98b8432..495b102a 100644
--- a/lower-constraints.txt
+++ b/lower-constraints.txt
@@ -38,7 +38,7 @@ msgpack-python==0.4.0
munch==2.1.0
netaddr==0.7.18
netifaces==0.10.4
-openstacksdk==0.56.0
+openstacksdk==0.61.0
os-client-config==2.1.0
os-service-types==1.7.0
osc-lib==2.3.0
diff --git a/openstackclient/common/quota.py b/openstackclient/common/quota.py
index 643cb4e4..677cba03 100644
--- a/openstackclient/common/quota.py
+++ b/openstackclient/common/quota.py
@@ -535,6 +535,12 @@ class SetQuota(common.NetDetectionMixin, command.Command):
action='store_true',
help=_('Force quota update (only supported by compute)')
)
+ parser.add_argument(
+ '--check-limit',
+ action='store_true',
+ help=_('Check quota limit when updating (only supported by '
+ 'network)')
+ )
return parser
def take_action(self, parsed_args):
@@ -561,6 +567,9 @@ class SetQuota(common.NetDetectionMixin, command.Command):
volume_kwargs[k] = value
network_kwargs = {}
+ if parsed_args.check_limit:
+ network_kwargs['check_limit'] = True
+
if self.app.client_manager.is_network_endpoint_enabled():
for k, v in NETWORK_QUOTAS.items():
value = getattr(parsed_args, k, None)
diff --git a/openstackclient/compute/v2/aggregate.py b/openstackclient/compute/v2/aggregate.py
index e39eb2d2..37522a78 100644
--- a/openstackclient/compute/v2/aggregate.py
+++ b/openstackclient/compute/v2/aggregate.py
@@ -193,12 +193,14 @@ class ListAggregate(command.Lister):
"Name",
"Availability Zone",
"Properties",
+ "Hosts",
)
columns = (
"ID",
"Name",
"Availability Zone",
"Metadata",
+ "Hosts",
)
else:
column_headers = columns = (
diff --git a/openstackclient/compute/v2/flavor.py b/openstackclient/compute/v2/flavor.py
index a55aba2a..8a9eb07a 100644
--- a/openstackclient/compute/v2/flavor.py
+++ b/openstackclient/compute/v2/flavor.py
@@ -48,7 +48,7 @@ def _get_flavor_columns(item):
'is_public': 'os-flavor-access:is_public'
}
- hidden_columns = ['links', 'location']
+ hidden_columns = ['links', 'location', 'original_name']
return utils.get_osc_show_columns_for_sdk_resource(
item, column_map, hidden_columns)
diff --git a/openstackclient/compute/v2/server.py b/openstackclient/compute/v2/server.py
index 08627f9b..a46fb904 100644
--- a/openstackclient/compute/v2/server.py
+++ b/openstackclient/compute/v2/server.py
@@ -204,7 +204,7 @@ def boolenv(*vars, default=False):
return default
-class AddFixedIP(command.Command):
+class AddFixedIP(command.ShowOne):
_description = _("Add fixed IP address to server")
def get_parser(self, prog_name):
@@ -231,36 +231,73 @@ class AddFixedIP(command.Command):
metavar='<tag>',
help=_(
'Tag for the attached interface. '
- '(supported by --os-compute-api-version 2.52 or above)'
+ '(supported by --os-compute-api-version 2.49 or above)'
)
)
return parser
def take_action(self, parsed_args):
- compute_client = self.app.client_manager.compute
-
- server = utils.find_resource(
- compute_client.servers, parsed_args.server)
-
- network = compute_client.api.network_find(parsed_args.network)
-
- kwargs = {
- 'port_id': None,
- 'net_id': network['id'],
- 'fixed_ip': parsed_args.fixed_ip_address,
- }
+ compute_client = self.app.client_manager.sdk_connection.compute
+ server = compute_client.find_server(
+ parsed_args.server,
+ ignore_missing=False
+ )
if parsed_args.tag:
- if compute_client.api_version < api_versions.APIVersion('2.49'):
+ if not sdk_utils.supports_microversion(compute_client, '2.49'):
msg = _(
'--os-compute-api-version 2.49 or greater is required to '
'support the --tag option'
)
raise exceptions.CommandError(msg)
+ if self.app.client_manager.is_network_endpoint_enabled():
+ network_client = self.app.client_manager.network
+ net_id = network_client.find_network(
+ parsed_args.network,
+ ignore_missing=False
+ ).id
+ else:
+ net_id = parsed_args.network
+
+ if not sdk_utils.supports_microversion(compute_client, '2.44'):
+ compute_client.add_fixed_ip_to_server(
+ server.id,
+ net_id
+ )
+ return ((), ())
+
+ kwargs = {
+ 'net_id': net_id,
+ 'fixed_ip': parsed_args.fixed_ip_address,
+ }
+ if parsed_args.tag:
kwargs['tag'] = parsed_args.tag
- server.interface_attach(**kwargs)
+ interface = compute_client.create_server_interface(server.id, **kwargs)
+
+ columns = (
+ 'port_id', 'server_id', 'net_id', 'mac_addr', 'port_state',
+ 'fixed_ips',
+ )
+ column_headers = (
+ 'Port ID', 'Server ID', 'Network ID', 'MAC Address', 'Port State',
+ 'Fixed IPs',
+ )
+ if sdk_utils.supports_microversion(compute_client, '2.49'):
+ columns += ('tag',)
+ column_headers += ('Tag',)
+
+ return (
+ column_headers,
+ utils.get_item_properties(
+ interface,
+ columns,
+ formatters={
+ 'fixed_ips': format_columns.ListDictColumn,
+ },
+ ),
+ )
class AddFloatingIP(network_common.NetworkAndComputeCommand):
@@ -548,24 +585,25 @@ class AddServerVolume(command.ShowOne):
return parser
def take_action(self, parsed_args):
- compute_client = self.app.client_manager.compute
- volume_client = self.app.client_manager.volume
+ compute_client = self.app.client_manager.sdk_connection.compute
+ volume_client = self.app.client_manager.sdk_connection.volume
- server = utils.find_resource(
- compute_client.servers,
+ server = compute_client.find_server(
parsed_args.server,
+ ignore_missing=False,
)
- volume = utils.find_resource(
- volume_client.volumes,
+ volume = volume_client.find_volume(
parsed_args.volume,
+ ignore_missing=False,
)
kwargs = {
+ "volumeId": volume.id,
"device": parsed_args.device
}
if parsed_args.tag:
- if compute_client.api_version < api_versions.APIVersion('2.49'):
+ if not sdk_utils.supports_microversion(compute_client, '2.49'):
msg = _(
'--os-compute-api-version 2.49 or greater is required to '
'support the --tag option'
@@ -575,7 +613,7 @@ class AddServerVolume(command.ShowOne):
kwargs['tag'] = parsed_args.tag
if parsed_args.enable_delete_on_termination:
- if compute_client.api_version < api_versions.APIVersion('2.79'):
+ if not sdk_utils.supports_microversion(compute_client, '2.79'):
msg = _(
'--os-compute-api-version 2.79 or greater is required to '
'support the --enable-delete-on-termination option.'
@@ -585,7 +623,7 @@ class AddServerVolume(command.ShowOne):
kwargs['delete_on_termination'] = True
if parsed_args.disable_delete_on_termination:
- if compute_client.api_version < api_versions.APIVersion('2.79'):
+ if not sdk_utils.supports_microversion(compute_client, '2.79'):
msg = _(
'--os-compute-api-version 2.79 or greater is required to '
'support the --disable-delete-on-termination option.'
@@ -594,28 +632,23 @@ class AddServerVolume(command.ShowOne):
kwargs['delete_on_termination'] = False
- volume_attachment = compute_client.volumes.create_server_volume(
- server.id,
- volume.id,
- **kwargs
+ volume_attachment = compute_client.create_volume_attachment(
+ server,
+ **kwargs,
)
- columns = ('id', 'serverId', 'volumeId', 'device')
+ columns = ('id', 'server id', 'volume id', 'device')
column_headers = ('ID', 'Server ID', 'Volume ID', 'Device')
- if compute_client.api_version >= api_versions.APIVersion('2.49'):
+ if sdk_utils.supports_microversion(compute_client, '2.49'):
columns += ('tag',)
column_headers += ('Tag',)
- if compute_client.api_version >= api_versions.APIVersion('2.79'):
+ if sdk_utils.supports_microversion(compute_client, '2.79'):
columns += ('delete_on_termination',)
column_headers += ('Delete On Termination',)
return (
column_headers,
- utils.get_item_properties(
- volume_attachment,
- columns,
- mixed_case_fields=('serverId', 'volumeId'),
- )
+ utils.get_item_properties(volume_attachment, columns,)
)
@@ -2208,15 +2241,19 @@ class ListServer(command.Lister):
# flavor name is given, map it to ID.
flavor_id = None
if parsed_args.flavor:
- flavor_id = utils.find_resource(compute_client.flavors,
- parsed_args.flavor).id
+ flavor_id = utils.find_resource(
+ compute_client.flavors,
+ parsed_args.flavor,
+ ).id
# Nova only supports list servers searching by image ID. So if a
# image name is given, map it to ID.
image_id = None
if parsed_args.image:
- image_id = image_client.find_image(parsed_args.image,
- ignore_missing=False).id
+ image_id = image_client.find_image(
+ parsed_args.image,
+ ignore_missing=False,
+ ).id
search_opts = {
'reservation_id': parsed_args.reservation_id,
@@ -2324,95 +2361,100 @@ class ListServer(command.Lister):
try:
iso8601.parse_date(search_opts['changes-since'])
except (TypeError, iso8601.ParseError):
+ msg = _('Invalid changes-since value: %s')
raise exceptions.CommandError(
- _('Invalid changes-since value: %s') %
- search_opts['changes-since']
+ msg % search_opts['changes-since']
)
+ columns = (
+ 'id',
+ 'name',
+ 'status',
+ )
+ column_headers = (
+ 'ID',
+ 'Name',
+ 'Status',
+ )
+
if parsed_args.long:
- columns = (
- 'ID',
- 'Name',
- 'Status',
+ columns += (
'OS-EXT-STS:task_state',
'OS-EXT-STS:power_state',
- 'Networks',
- 'Image Name',
- 'Image ID',
- 'Flavor Name',
- 'Flavor ID',
- 'OS-EXT-AZ:availability_zone',
- 'OS-EXT-SRV-ATTR:host',
- 'Metadata',
)
- column_headers = (
- 'ID',
- 'Name',
- 'Status',
+ column_headers += (
'Task State',
'Power State',
- 'Networks',
+ )
+
+ columns += ('networks',)
+ column_headers += ('Networks',)
+
+ if parsed_args.long:
+ columns += (
+ 'image_name',
+ 'image_id',
+ )
+ column_headers += (
'Image Name',
'Image ID',
- 'Flavor Name',
- 'Flavor ID',
- 'Availability Zone',
- 'Host',
- 'Properties',
)
- mixed_case_fields = [
- 'OS-EXT-STS:task_state',
- 'OS-EXT-STS:power_state',
- 'OS-EXT-AZ:availability_zone',
- 'OS-EXT-SRV-ATTR:host',
- ]
else:
if parsed_args.no_name_lookup:
- columns = (
- 'ID',
- 'Name',
- 'Status',
- 'Networks',
- 'Image ID',
- 'Flavor ID',
- )
+ columns += ('image_id',)
else:
- columns = (
- 'ID',
- 'Name',
- 'Status',
- 'Networks',
- 'Image Name',
+ columns += ('image_name',)
+ column_headers += ('Image',)
+
+ # microversion 2.47 puts the embedded flavor into the server response
+ # body but omits the id, so if not present we just expose the original
+ # flavor name in the output
+ if compute_client.api_version >= api_versions.APIVersion('2.47'):
+ columns += ('flavor_name',)
+ column_headers += ('Flavor',)
+ else:
+ if parsed_args.long:
+ columns += (
+ 'flavor_name',
+ 'flavor_id',
+ )
+ column_headers += (
'Flavor Name',
+ 'Flavor ID',
)
- column_headers = (
- 'ID',
- 'Name',
- 'Status',
- 'Networks',
- 'Image',
- 'Flavor',
+ else:
+ if parsed_args.no_name_lookup:
+ columns += ('flavor_id',)
+ else:
+ columns += ('flavor_name',)
+ column_headers += ('Flavor',)
+
+ if parsed_args.long:
+ columns += (
+ 'OS-EXT-AZ:availability_zone',
+ 'OS-EXT-SRV-ATTR:host',
+ 'metadata',
+ )
+ column_headers += (
+ 'Availability Zone',
+ 'Host',
+ 'Properties',
)
- mixed_case_fields = []
marker_id = None
# support for additional columns
if parsed_args.columns:
- # convert tuple to list to edit them
- column_headers = list(column_headers)
- columns = list(columns)
-
for c in parsed_args.columns:
if c in ('Project ID', 'project_id'):
- columns.append('tenant_id')
- column_headers.append('Project ID')
+ columns += ('tenant_id',)
+ column_headers += ('Project ID',)
if c in ('User ID', 'user_id'):
- columns.append('user_id')
- column_headers.append('User ID')
+ columns += ('user_id',)
+ column_headers += ('User ID',)
if c in ('Created At', 'created_at'):
- columns.append('created')
- column_headers.append('Created At')
+ columns += ('created',)
+ column_headers += ('Created At',)
# convert back to tuple
column_headers = tuple(column_headers)
@@ -2426,25 +2468,29 @@ class ListServer(command.Lister):
if parsed_args.deleted:
marker_id = parsed_args.marker
else:
- marker_id = utils.find_resource(compute_client.servers,
- parsed_args.marker).id
+ marker_id = utils.find_resource(
+ compute_client.servers,
+ parsed_args.marker,
+ ).id
- data = compute_client.servers.list(search_opts=search_opts,
- marker=marker_id,
- limit=parsed_args.limit)
+ data = compute_client.servers.list(
+ search_opts=search_opts,
+ marker=marker_id,
+ limit=parsed_args.limit)
images = {}
flavors = {}
if data and not parsed_args.no_name_lookup:
- # Create a dict that maps image_id to image object.
- # Needed so that we can display the "Image Name" column.
- # "Image Name" is not crucial, so we swallow any exceptions.
- # The 'image' attribute can be an empty string if the server was
- # booted from a volume.
+ # create a dict that maps image_id to image object, which is used
+ # to display the "Image Name" column. Note that 'image.id' can be
+ # empty for BFV instances and 'image' can be missing entirely if
+ # there are infra failures
if parsed_args.name_lookup_one_by_one or image_id:
- for i_id in set(filter(lambda x: x is not None,
- (s.image.get('id') for s in data
- if s.image))):
+ for i_id in set(
+ s.image['id'] for s in data
+ if s.image and s.image.get('id')
+ ):
+ # "Image Name" is not crucial, so we swallow any exceptions
try:
images[i_id] = image_client.get_image(i_id)
except Exception:
@@ -2457,12 +2503,17 @@ class ListServer(command.Lister):
except Exception:
pass
- # Create a dict that maps flavor_id to flavor object.
- # Needed so that we can display the "Flavor Name" column.
- # "Flavor Name" is not crucial, so we swallow any exceptions.
+ # create a dict that maps flavor_id to flavor object, which is used
+ # to display the "Flavor Name" column. Note that 'flavor.id' is not
+ # present on microversion 2.47 or later and 'flavor' won't be
+ # present if there are infra failures
if parsed_args.name_lookup_one_by_one or flavor_id:
- for f_id in set(filter(lambda x: x is not None,
- (s.flavor.get('id') for s in data))):
+ for f_id in set(
+ s.flavor['id'] for s in data
+ if s.flavor and s.flavor.get('id')
+ ):
+ # "Flavor Name" is not crucial, so we swallow any
+ # exceptions
try:
flavors[f_id] = compute_client.flavors.get(f_id)
except Exception:
@@ -2486,6 +2537,7 @@ class ListServer(command.Lister):
# processing of the image and flavor informations.
if not hasattr(s, 'image') or not hasattr(s, 'flavor'):
continue
+
if 'id' in s.image:
image = images.get(s.image['id'])
if image:
@@ -2498,29 +2550,30 @@ class ListServer(command.Lister):
# able to grep for boot-from-volume servers when using the CLI.
s.image_name = IMAGE_STRING_FOR_BFV
s.image_id = IMAGE_STRING_FOR_BFV
- if 'id' in s.flavor:
+
+ if compute_client.api_version < api_versions.APIVersion('2.47'):
flavor = flavors.get(s.flavor['id'])
if flavor:
s.flavor_name = flavor.name
s.flavor_id = s.flavor['id']
else:
- # TODO(mriedem): Fix this for microversion >= 2.47 where the
- # flavor is embedded in the server response without the id.
- # We likely need to drop the Flavor ID column in that case if
- # --long is specified.
- s.flavor_name = ''
- s.flavor_id = ''
+ s.flavor_name = s.flavor['original_name']
table = (
column_headers,
(
utils.get_item_properties(
s, columns,
- mixed_case_fields=mixed_case_fields,
+ mixed_case_fields=(
+ 'OS-EXT-STS:task_state',
+ 'OS-EXT-STS:power_state',
+ 'OS-EXT-AZ:availability_zone',
+ 'OS-EXT-SRV-ATTR:host',
+ ),
formatters={
'OS-EXT-STS:power_state': PowerStateColumn,
- 'Networks': format_columns.DictListColumn,
- 'Metadata': format_columns.DictColumn,
+ 'networks': format_columns.DictListColumn,
+ 'metadata': format_columns.DictColumn,
},
) for s in data
),
@@ -2638,7 +2691,7 @@ revert to release the new server and restart the old one.""")
disk_group.add_argument(
'--disk-overcommit',
action='store_true',
- default=False,
+ default=None,
help=_(
'Allow disk over-commit on the destination host'
'(supported with --os-compute-api-version 2.24 or below)'
@@ -2648,7 +2701,6 @@ revert to release the new server and restart the old one.""")
'--no-disk-overcommit',
dest='disk_overcommit',
action='store_false',
- default=False,
help=_(
'Do not over-commit disk on the destination host (default)'
'(supported with --os-compute-api-version 2.24 or below)'
@@ -2710,6 +2762,11 @@ revert to release the new server and restart the old one.""")
if compute_client.api_version < api_versions.APIVersion('2.25'):
kwargs['disk_over_commit'] = parsed_args.disk_overcommit
+ # We can't use an argparse default value because then we can't
+ # distinguish between explicit 'False' and unset for the below
+ # case (microversion >= 2.25)
+ if kwargs['disk_over_commit'] is None:
+ kwargs['disk_over_commit'] = False
elif parsed_args.disk_overcommit is not None:
# TODO(stephenfin): Raise an error here in OSC 7.0
msg = _(
@@ -3134,12 +3191,13 @@ class PauseServer(command.Command):
return parser
def take_action(self, parsed_args):
- compute_client = self.app.client_manager.compute
+ compute_client = self.app.client_manager.sdk_connection.compute
for server in parsed_args.server:
- utils.find_resource(
- compute_client.servers,
- server
- ).pause()
+ server_id = compute_client.find_server(
+ server,
+ ignore_missing=False,
+ ).id
+ compute_client.pause_server(server_id)
class RebootServer(command.Command):
@@ -3694,10 +3752,10 @@ class RemovePort(command.Command):
return parser
def take_action(self, parsed_args):
- compute_client = self.app.client_manager.compute
+ compute_client = self.app.client_manager.sdk_connection.compute
- server = utils.find_resource(
- compute_client.servers, parsed_args.server)
+ server = compute_client.find_server(
+ parsed_args.server, ignore_missing=False)
if self.app.client_manager.is_network_endpoint_enabled():
network_client = self.app.client_manager.network
@@ -3706,7 +3764,11 @@ class RemovePort(command.Command):
else:
port_id = parsed_args.port
- server.interface_detach(port_id)
+ compute_client.delete_server_interface(
+ port_id,
+ server=server,
+ ignore_missing=False,
+ )
class RemoveNetwork(command.Command):
@@ -3727,10 +3789,10 @@ class RemoveNetwork(command.Command):
return parser
def take_action(self, parsed_args):
- compute_client = self.app.client_manager.compute
+ compute_client = self.app.client_manager.sdk_connection.compute
- server = utils.find_resource(
- compute_client.servers, parsed_args.server)
+ server = compute_client.find_server(
+ parsed_args.server, ignore_missing=False)
if self.app.client_manager.is_network_endpoint_enabled():
network_client = self.app.client_manager.network
@@ -3739,9 +3801,12 @@ class RemoveNetwork(command.Command):
else:
net_id = parsed_args.network
- for inf in server.interface_list():
+ for inf in compute_client.server_interfaces(server):
if inf.net_id == net_id:
- server.interface_detach(inf.port_id)
+ compute_client.delete_server_interface(
+ inf.port_id,
+ server=server,
+ )
class RemoveServerSecurityGroup(command.Command):
@@ -3797,21 +3862,22 @@ class RemoveServerVolume(command.Command):
return parser
def take_action(self, parsed_args):
- compute_client = self.app.client_manager.compute
- volume_client = self.app.client_manager.volume
+ compute_client = self.app.client_manager.sdk_connection.compute
+ volume_client = self.app.client_manager.sdk_connection.volume
- server = utils.find_resource(
- compute_client.servers,
+ server = compute_client.find_server(
parsed_args.server,
+ ignore_missing=False,
)
- volume = utils.find_resource(
- volume_client.volumes,
+ volume = volume_client.find_volume(
parsed_args.volume,
+ ignore_missing=False,
)
- compute_client.volumes.delete_server_volume(
- server.id,
- volume.id,
+ compute_client.delete_volume_attachment(
+ volume,
+ server,
+ ignore_missing=False,
)
@@ -4077,13 +4143,13 @@ class ResumeServer(command.Command):
return parser
def take_action(self, parsed_args):
-
- compute_client = self.app.client_manager.compute
+ compute_client = self.app.client_manager.sdk_connection.compute
for server in parsed_args.server:
- utils.find_resource(
- compute_client.servers,
+ server_id = compute_client.find_server(
server,
- ).resume()
+ ignore_missing=False,
+ ).id
+ compute_client.resume_server(server_id)
class SetServer(command.Command):
@@ -4436,50 +4502,29 @@ class SshServer(command.Command):
metavar='<server>',
help=_('Server (name or ID)'),
)
+ # Deprecated during the Yoga cycle
parser.add_argument(
- '--login',
- metavar='<login-name>',
- help=_('Login name (ssh -l option)'),
- )
- parser.add_argument(
- '-l',
- dest='login',
+ '--login', '-l',
metavar='<login-name>',
help=argparse.SUPPRESS,
)
+ # Deprecated during the Yoga cycle
parser.add_argument(
- '--port',
+ '--port', '-p',
metavar='<port>',
type=int,
- help=_('Destination port (ssh -p option)'),
- )
- parser.add_argument(
- '-p',
- metavar='<port>',
- dest='port',
- type=int,
help=argparse.SUPPRESS,
)
+ # Deprecated during the Yoga cycle
parser.add_argument(
- '--identity',
+ '--identity', '-i',
metavar='<keyfile>',
- help=_('Private key file (ssh -i option)'),
- )
- parser.add_argument(
- '-i',
- metavar='<filename>',
- dest='identity',
help=argparse.SUPPRESS,
)
+ # Deprecated during the Yoga cycle
parser.add_argument(
- '--option',
+ '--option', '-o',
metavar='<config-options>',
- help=_('Options in ssh_config(5) format (ssh -o option)'),
- )
- parser.add_argument(
- '-o',
- metavar='<option>',
- dest='option',
help=argparse.SUPPRESS,
)
ip_group = parser.add_mutually_exclusive_group()
@@ -4521,6 +4566,7 @@ class SshServer(command.Command):
default='public',
help=_('Use other IP address (public, private, etc)'),
)
+ # Deprecated during the Yoga cycle
parser.add_argument(
'-v',
dest='verbose',
@@ -4528,46 +4574,77 @@ class SshServer(command.Command):
default=False,
help=argparse.SUPPRESS,
)
+ parser.add_argument(
+ 'ssh_args',
+ nargs='*',
+ metavar='-- <standard ssh args>',
+ help=(
+ 'Any argument or option that ssh allows. '
+ 'Use -- once between openstackclient args and SSH args.'
+ ),
+ )
return parser
def take_action(self, parsed_args):
compute_client = self.app.client_manager.compute
+
server = utils.find_resource(
compute_client.servers,
parsed_args.server,
)
- # Build the command
- cmd = "ssh"
+ # first, handle the deprecated options
+ if any((
+ parsed_args.port,
+ parsed_args.identity,
+ parsed_args.option,
+ parsed_args.login,
+ parsed_args.verbose,
+ )):
+ msg = _(
+ 'The ssh options have been deprecated. The ssh equivalent '
+ 'options can be used instead as arguments after "--" on '
+ 'the command line.'
+ )
+ self.log.warning(msg)
ip_address_family = [4, 6]
if parsed_args.ipv4:
ip_address_family = [4]
- cmd += " -4"
if parsed_args.ipv6:
ip_address_family = [6]
- cmd += " -6"
+
+ args = parsed_args.ssh_args[:]
if parsed_args.port:
- cmd += " -p %d" % parsed_args.port
+ args.extend(['-p', str(parsed_args.port)])
+
if parsed_args.identity:
- cmd += " -i %s" % parsed_args.identity
+ args.extend(['-i', parsed_args.identity])
+
if parsed_args.option:
- cmd += " -o %s" % parsed_args.option
+ args.extend(['-o', parsed_args.option])
+
if parsed_args.login:
login = parsed_args.login
- else:
+ args.extend(['-l', login])
+ elif '-l' not in args:
login = self.app.client_manager.auth_ref.username
+ args.extend(['-l', login])
+
if parsed_args.verbose:
- cmd += " -v"
+ args.append('-v')
+
+ ip_address = _get_ip_address(
+ server.addresses,
+ parsed_args.address_type,
+ ip_address_family,
+ )
- cmd += " %s@%s"
- ip_address = _get_ip_address(server.addresses,
- parsed_args.address_type,
- ip_address_family)
- LOG.debug("ssh command: %s", (cmd % (login, ip_address)))
- os.system(cmd % (login, ip_address))
+ cmd = ' '.join(['ssh', ip_address] + args)
+ LOG.debug("ssh command: {cmd}".format(cmd=cmd))
+ os.system(cmd)
class StartServer(command.Command):
@@ -4648,13 +4725,13 @@ class SuspendServer(command.Command):
return parser
def take_action(self, parsed_args):
-
- compute_client = self.app.client_manager.compute
+ compute_client = self.app.client_manager.sdk_connection.compute
for server in parsed_args.server:
- utils.find_resource(
- compute_client.servers,
+ server_id = compute_client.find_server(
server,
- ).suspend()
+ ignore_missing=False,
+ ).id
+ compute_client.suspend_server(server_id)
class UnlockServer(command.Command):
@@ -4671,7 +4748,6 @@ class UnlockServer(command.Command):
return parser
def take_action(self, parsed_args):
-
compute_client = self.app.client_manager.compute
for server in parsed_args.server:
utils.find_resource(
@@ -4694,13 +4770,13 @@ class UnpauseServer(command.Command):
return parser
def take_action(self, parsed_args):
-
- compute_client = self.app.client_manager.compute
+ compute_client = self.app.client_manager.sdk_connection.compute
for server in parsed_args.server:
- utils.find_resource(
- compute_client.servers,
+ server_id = compute_client.find_server(
server,
- ).unpause()
+ ignore_missing=False,
+ ).id
+ compute_client.unpause_server(server_id)
class UnrescueServer(command.Command):
diff --git a/openstackclient/compute/v2/server_image.py b/openstackclient/compute/v2/server_image.py
index 6c0e3b22..2021fae7 100644
--- a/openstackclient/compute/v2/server_image.py
+++ b/openstackclient/compute/v2/server_image.py
@@ -73,25 +73,23 @@ class CreateServerImage(command.ShowOne):
self.app.stdout.write('\rProgress: %s' % progress)
self.app.stdout.flush()
- compute_client = self.app.client_manager.compute
+ compute_client = self.app.client_manager.sdk_connection.compute
+ image_client = self.app.client_manager.image
- server = utils.find_resource(
- compute_client.servers,
- parsed_args.server,
+ server = compute_client.find_server(
+ parsed_args.server, ignore_missing=False,
)
+
if parsed_args.name:
image_name = parsed_args.name
else:
image_name = server.name
- image_id = compute_client.servers.create_image(
+ image_id = compute_client.create_server_image(
server.id,
image_name,
parsed_args.properties,
- )
-
- image_client = self.app.client_manager.image
- image = image_client.find_image(image_id)
+ ).id
if parsed_args.wait:
if utils.wait_for_status(
@@ -105,6 +103,8 @@ class CreateServerImage(command.ShowOne):
_('Error creating server image: %s'), parsed_args.server)
raise exceptions.CommandError
+ image = image_client.find_image(image_id, ignore_missing=False)
+
if self.app.client_manager._api_version['image'] == '1':
info = {}
info.update(image._info)
diff --git a/openstackclient/compute/v2/service.py b/openstackclient/compute/v2/service.py
index 6427e548..8605156c 100644
--- a/openstackclient/compute/v2/service.py
+++ b/openstackclient/compute/v2/service.py
@@ -17,7 +17,7 @@
import logging
-from novaclient import api_versions
+from openstack import utils as sdk_utils
from osc_lib.command import command
from osc_lib import exceptions
from osc_lib import utils
@@ -40,16 +40,24 @@ class DeleteService(command.Command):
help=_("Compute service(s) to delete (ID only). If using "
"``--os-compute-api-version`` 2.53 or greater, the ID is "
"a UUID which can be retrieved by listing compute services "
- "using the same 2.53+ microversion.")
+ "using the same 2.53+ microversion. "
+ "If deleting a compute service, be sure to stop the actual "
+ "compute process on the physical host before deleting the "
+ "service with this command. Failing to do so can lead to "
+ "the running service re-creating orphaned compute_nodes "
+ "table records in the database.")
)
return parser
def take_action(self, parsed_args):
- compute_client = self.app.client_manager.compute
+ compute_client = self.app.client_manager.sdk_connection.compute
result = 0
for s in parsed_args.service:
try:
- compute_client.services.delete(s)
+ compute_client.delete_service(
+ s,
+ ignore_missing=False
+ )
except Exception as e:
result += 1
LOG.error(_("Failed to delete compute service with "
@@ -91,38 +99,40 @@ class ListService(command.Lister):
return parser
def take_action(self, parsed_args):
- compute_client = self.app.client_manager.compute
+ compute_client = self.app.client_manager.sdk_connection.compute
+ columns = (
+ "id",
+ "binary",
+ "host",
+ "availability_zone",
+ "status",
+ "state",
+ "updated_at",
+ )
+ column_headers = (
+ "ID",
+ "Binary",
+ "Host",
+ "Zone",
+ "Status",
+ "State",
+ "Updated At",
+ )
if parsed_args.long:
- columns = (
- "ID",
- "Binary",
- "Host",
- "Zone",
- "Status",
- "State",
- "Updated At",
- "Disabled Reason"
- )
- has_forced_down = (
- compute_client.api_version >= api_versions.APIVersion('2.11'))
- if has_forced_down:
- columns += ("Forced Down",)
- else:
- columns = (
- "ID",
- "Binary",
- "Host",
- "Zone",
- "Status",
- "State",
- "Updated At"
- )
- data = compute_client.services.list(parsed_args.host,
- parsed_args.service)
- return (columns,
- (utils.get_item_properties(
- s, columns,
- ) for s in data))
+ columns += ("disabled_reason",)
+ column_headers += ("Disabled Reason",)
+ if sdk_utils.supports_microversion(compute_client, '2.11'):
+ columns += ("is_forced_down",)
+ column_headers += ("Forced Down",)
+
+ data = compute_client.services(
+ host=parsed_args.host,
+ binary=parsed_args.service
+ )
+ return (
+ column_headers,
+ (utils.get_item_properties(s, columns) for s in data)
+ )
class SetService(command.Command):
@@ -175,7 +185,7 @@ class SetService(command.Command):
return parser
@staticmethod
- def _find_service_by_host_and_binary(cs, host, binary):
+ def _find_service_by_host_and_binary(compute_client, host, binary):
"""Utility method to find a compute service by host and binary
:param host: the name of the compute service host
@@ -183,7 +193,7 @@ class SetService(command.Command):
:returns: novaclient.v2.services.Service dict-like object
:raises: CommandError if no or multiple results were found
"""
- services = cs.list(host=host, binary=binary)
+ services = list(compute_client.services(host=host, binary=binary))
# Did we find anything?
if not len(services):
msg = _('Compute service for host "%(host)s" and binary '
@@ -202,8 +212,7 @@ class SetService(command.Command):
return services[0]
def take_action(self, parsed_args):
- compute_client = self.app.client_manager.compute
- cs = compute_client.services
+ compute_client = self.app.client_manager.sdk_connection.compute
if (parsed_args.enable or not parsed_args.disable) and \
parsed_args.disable_reason:
@@ -216,14 +225,17 @@ class SetService(command.Command):
# services. If 2.53+ is used we need to find the nova-compute
# service using the --host and --service (binary) values.
requires_service_id = (
- compute_client.api_version >= api_versions.APIVersion('2.53'))
+ sdk_utils.supports_microversion(compute_client, '2.53'))
service_id = None
if requires_service_id:
# TODO(mriedem): Add an --id option so users can pass the service
# id (as a uuid) directly rather than make us look it up using
# host/binary.
service_id = SetService._find_service_by_host_and_binary(
- cs, parsed_args.host, parsed_args.service).id
+ compute_client,
+ parsed_args.host,
+ parsed_args.service
+ ).id
result = 0
enabled = None
@@ -235,21 +247,18 @@ class SetService(command.Command):
if enabled is not None:
if enabled:
- args = (service_id,) if requires_service_id else (
- parsed_args.host, parsed_args.service)
- cs.enable(*args)
+ compute_client.enable_service(
+ service_id,
+ parsed_args.host,
+ parsed_args.service
+ )
else:
- if parsed_args.disable_reason:
- args = (service_id, parsed_args.disable_reason) if \
- requires_service_id else (
- parsed_args.host,
- parsed_args.service,
- parsed_args.disable_reason)
- cs.disable_log_reason(*args)
- else:
- args = (service_id,) if requires_service_id else (
- parsed_args.host, parsed_args.service)
- cs.disable(*args)
+ compute_client.disable_service(
+ service_id,
+ parsed_args.host,
+ parsed_args.service,
+ parsed_args.disable_reason
+ )
except Exception:
status = "enabled" if enabled else "disabled"
LOG.error("Failed to set service status to %s", status)
@@ -261,15 +270,17 @@ class SetService(command.Command):
if parsed_args.up:
force_down = False
if force_down is not None:
- if compute_client.api_version < api_versions.APIVersion(
- '2.11'):
+ if not sdk_utils.supports_microversion(compute_client, '2.11'):
msg = _('--os-compute-api-version 2.11 or later is '
'required')
raise exceptions.CommandError(msg)
try:
- args = (service_id, force_down) if requires_service_id else (
- parsed_args.host, parsed_args.service, force_down)
- cs.force_down(*args)
+ compute_client.update_service_forced_down(
+ service_id,
+ parsed_args.host,
+ parsed_args.service,
+ force_down
+ )
except Exception:
state = "down" if force_down else "up"
LOG.error("Failed to set service state to %s", state)
diff --git a/openstackclient/network/v2/address_group.py b/openstackclient/network/v2/address_group.py
index 9017047f..b08fa872 100644
--- a/openstackclient/network/v2/address_group.py
+++ b/openstackclient/network/v2/address_group.py
@@ -28,10 +28,13 @@ LOG = logging.getLogger(__name__)
def _get_columns(item):
- column_map = {
- 'tenant_id': 'project_id',
- }
- return utils.get_osc_show_columns_for_sdk_resource(item, column_map)
+ column_map = {}
+ hidden_columns = ['location']
+ return utils.get_osc_show_columns_for_sdk_resource(
+ item,
+ column_map,
+ hidden_columns
+ )
def _format_addresses(addresses):
@@ -51,7 +54,7 @@ def _get_attrs(client_manager, parsed_args):
parsed_args.project,
parsed_args.project_domain,
).id
- attrs['tenant_id'] = project_id
+ attrs['project_id'] = project_id
return attrs
diff --git a/openstackclient/network/v2/address_scope.py b/openstackclient/network/v2/address_scope.py
index 5748793a..488e1600 100644
--- a/openstackclient/network/v2/address_scope.py
+++ b/openstackclient/network/v2/address_scope.py
@@ -29,9 +29,13 @@ LOG = logging.getLogger(__name__)
def _get_columns(item):
column_map = {
'is_shared': 'shared',
- 'tenant_id': 'project_id',
}
- return utils.get_osc_show_columns_for_sdk_resource(item, column_map)
+ hidden_columns = ['location']
+ return utils.get_osc_show_columns_for_sdk_resource(
+ item,
+ column_map,
+ hidden_columns
+ )
def _get_attrs(client_manager, parsed_args):
@@ -49,7 +53,7 @@ def _get_attrs(client_manager, parsed_args):
parsed_args.project,
parsed_args.project_domain,
).id
- attrs['tenant_id'] = project_id
+ attrs['project_id'] = project_id
return attrs
@@ -215,7 +219,6 @@ class ListAddressScope(command.Lister):
parsed_args.project,
parsed_args.project_domain,
).id
- attrs['tenant_id'] = project_id
attrs['project_id'] = project_id
data = client.address_scopes(**attrs)
diff --git a/openstackclient/network/v2/floating_ip.py b/openstackclient/network/v2/floating_ip.py
index 0951565c..4c03074d 100644
--- a/openstackclient/network/v2/floating_ip.py
+++ b/openstackclient/network/v2/floating_ip.py
@@ -27,17 +27,17 @@ _formatters = {
def _get_network_columns(item):
- column_map = {
- 'tenant_id': 'project_id',
- }
- return utils.get_osc_show_columns_for_sdk_resource(item, column_map)
+ column_map = {}
+ hidden_columns = ['location']
+ return utils.get_osc_show_columns_for_sdk_resource(
+ item,
+ column_map,
+ hidden_columns
+ )
def _get_columns(item):
columns = list(item.keys())
- if 'tenant_id' in columns:
- columns.remove('tenant_id')
- columns.append('project_id')
return tuple(sorted(columns))
@@ -81,7 +81,7 @@ def _get_attrs(client_manager, parsed_args):
parsed_args.project,
parsed_args.project_domain,
).id
- attrs['tenant_id'] = project_id
+ attrs['project_id'] = project_id
if parsed_args.dns_domain:
attrs['dns_domain'] = parsed_args.dns_domain
@@ -349,7 +349,6 @@ class ListFloatingIP(common.NetworkAndComputeLister):
parsed_args.project,
parsed_args.project_domain,
)
- query['tenant_id'] = project.id
query['project_id'] = project.id
if parsed_args.router is not None:
router = network_client.find_router(parsed_args.router,
diff --git a/openstackclient/network/v2/floating_ip_port_forwarding.py b/openstackclient/network/v2/floating_ip_port_forwarding.py
index f137174c..b33633d3 100644
--- a/openstackclient/network/v2/floating_ip_port_forwarding.py
+++ b/openstackclient/network/v2/floating_ip_port_forwarding.py
@@ -26,10 +26,13 @@ LOG = logging.getLogger(__name__)
def _get_columns(item):
- column_map = {
- 'tenant_id': 'project_id',
- }
- return utils.get_osc_show_columns_for_sdk_resource(item, column_map)
+ column_map = {}
+ hidden_columns = ['location']
+ return utils.get_osc_show_columns_for_sdk_resource(
+ item,
+ column_map,
+ hidden_columns
+ )
class CreateFloatingIPPortForwarding(command.ShowOne,
diff --git a/openstackclient/network/v2/ip_availability.py b/openstackclient/network/v2/ip_availability.py
index 6a3c67e2..9e56c5bd 100644
--- a/openstackclient/network/v2/ip_availability.py
+++ b/openstackclient/network/v2/ip_availability.py
@@ -26,10 +26,13 @@ _formatters = {
def _get_columns(item):
- column_map = {
- 'tenant_id': 'project_id',
- }
- return utils.get_osc_show_columns_for_sdk_resource(item, column_map)
+ column_map = {}
+ hidden_columns = ['location']
+ return utils.get_osc_show_columns_for_sdk_resource(
+ item,
+ column_map,
+ hidden_columns
+ )
# TODO(ankur-gupta-f): Use the SDK resource mapped attribute names once
@@ -84,7 +87,6 @@ class ListIPAvailability(command.Lister):
parsed_args.project,
parsed_args.project_domain,
).id
- filters['tenant_id'] = project_id
filters['project_id'] = project_id
data = client.network_ip_availabilities(**filters)
return (column_headers,
diff --git a/openstackclient/network/v2/l3_conntrack_helper.py b/openstackclient/network/v2/l3_conntrack_helper.py
index 9fc33d8f..1de5b785 100644
--- a/openstackclient/network/v2/l3_conntrack_helper.py
+++ b/openstackclient/network/v2/l3_conntrack_helper.py
@@ -26,7 +26,12 @@ LOG = logging.getLogger(__name__)
def _get_columns(item):
column_map = {}
- return utils.get_osc_show_columns_for_sdk_resource(item, column_map)
+ hidden_columns = ['location']
+ return utils.get_osc_show_columns_for_sdk_resource(
+ item,
+ column_map,
+ hidden_columns
+ )
def _get_attrs(client, parsed_args):
diff --git a/openstackclient/network/v2/local_ip.py b/openstackclient/network/v2/local_ip.py
new file mode 100644
index 00000000..109f64cf
--- /dev/null
+++ b/openstackclient/network/v2/local_ip.py
@@ -0,0 +1,310 @@
+# Copyright 2021 Huawei, Inc. 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.
+#
+
+"""Node Local IP action implementations"""
+
+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
+
+LOG = logging.getLogger(__name__)
+
+
+def _get_columns(item):
+ column_map = {}
+ hidden_columns = ['location']
+ return utils.get_osc_show_columns_for_sdk_resource(
+ item, column_map, hidden_columns)
+
+
+def _get_attrs(client_manager, parsed_args):
+ attrs = {}
+ network_client = client_manager.network
+
+ if parsed_args.name:
+ attrs['name'] = parsed_args.name
+ if parsed_args.description:
+ attrs['description'] = parsed_args.description
+ if 'project' in parsed_args and parsed_args.project is not None:
+ identity_client = client_manager.identity
+ project_id = identity_common.find_project(
+ identity_client,
+ parsed_args.project,
+ parsed_args.project_domain,
+ ).id
+ attrs['project_id'] = project_id
+ if parsed_args.network:
+ network = network_client.find_network(parsed_args.network,
+ ignore_missing=False)
+ attrs['network_id'] = network.id
+ if parsed_args.local_ip_address:
+ attrs['local_ip_address'] = parsed_args.local_ip_address
+ if parsed_args.local_port:
+ port = network_client.find_port(parsed_args.local_port,
+ ignore_missing=False)
+ attrs['local_port_id'] = port.id
+ if parsed_args.ip_mode:
+ attrs['ip_mode'] = parsed_args.ip_mode
+ return attrs
+
+
+class CreateLocalIP(command.ShowOne):
+ _description = _("Create Local IP")
+
+ def get_parser(self, prog_name):
+ parser = super().get_parser(prog_name)
+ parser.add_argument(
+ '--name',
+ metavar="<name>",
+ help=_("New local IP name")
+ )
+ parser.add_argument(
+ '--description',
+ metavar="<description>",
+ help=_("New local IP description")
+ )
+ parser.add_argument(
+ '--network',
+ metavar='<network>',
+ help=_("Network to allocate Local IP (name or ID)")
+ )
+ parser.add_argument(
+ '--local-port',
+ metavar='<local_port>',
+ help=_("Port to allocate Local IP (name or ID)")
+ )
+ parser.add_argument(
+ "--local-ip-address",
+ metavar="<local_ip_address>",
+ help=_("IP address or CIDR "),
+ )
+ parser.add_argument(
+ '--ip-mode',
+ metavar='<ip_mode>',
+ help=_("local IP ip mode")
+ )
+
+ identity_common.add_project_domain_option_to_parser(parser)
+
+ return parser
+
+ def take_action(self, parsed_args):
+ client = self.app.client_manager.network
+ attrs = _get_attrs(self.app.client_manager, parsed_args)
+
+ obj = client.create_local_ip(**attrs)
+ display_columns, columns = _get_columns(obj)
+ data = utils.get_item_properties(obj, columns, formatters={})
+
+ return (display_columns, data)
+
+
+class DeleteLocalIP(command.Command):
+ _description = _("Delete local IP(s)")
+
+ def get_parser(self, prog_name):
+ parser = super().get_parser(prog_name)
+ parser.add_argument(
+ 'local_ip',
+ metavar="<local-ip>",
+ nargs='+',
+ help=_("Local IP(s) to delete (name or ID)")
+ )
+
+ return parser
+
+ def take_action(self, parsed_args):
+ client = self.app.client_manager.network
+ result = 0
+
+ for lip in parsed_args.local_ip:
+ try:
+ obj = client.find_local_ip(lip, ignore_missing=False)
+ client.delete_local_ip(obj)
+ except Exception as e:
+ result += 1
+ LOG.error(_("Failed to delete Local IP with "
+ "name or ID '%(lip)s': %(e)s"),
+ {'lip': lip, 'e': e})
+
+ if result > 0:
+ total = len(parsed_args.local_ip)
+ msg = (_("%(result)s of %(total)s local IPs failed "
+ "to delete.") % {'result': result, 'total': total})
+ raise exceptions.CommandError(msg)
+
+
+class SetLocalIP(command.Command):
+ _description = _("Set local ip properties")
+
+ def get_parser(self, prog_name):
+ parser = super().get_parser(prog_name)
+ parser.add_argument(
+ 'local_ip',
+ metavar="<local-ip>",
+ help=_("Local IP to modify (name or ID)")
+ )
+ parser.add_argument(
+ '--name',
+ metavar="<name>",
+ help=_('Set local IP name')
+ )
+ parser.add_argument(
+ '--description',
+ metavar="<description>",
+ help=_('Set local IP description')
+ )
+ return parser
+
+ def take_action(self, parsed_args):
+ client = self.app.client_manager.network
+ obj = client.find_local_ip(
+ parsed_args.local_ip,
+ ignore_missing=False)
+ attrs = {}
+ if parsed_args.name is not None:
+ attrs['name'] = parsed_args.name
+ if parsed_args.description is not None:
+ attrs['description'] = parsed_args.description
+ if attrs:
+ client.update_local_ip(obj, **attrs)
+
+
+class ListLocalIP(command.Lister):
+ _description = _("List local IPs")
+
+ def get_parser(self, prog_name):
+ parser = super().get_parser(prog_name)
+
+ parser.add_argument(
+ '--name',
+ metavar='<name>',
+ help=_("List only local IPs of given name in output")
+ )
+ parser.add_argument(
+ '--project',
+ metavar="<project>",
+ help=_("List Local IPs according to their project "
+ "(name or ID)")
+ )
+ parser.add_argument(
+ '--network',
+ metavar='<network>',
+ help=_("List Local IP(s) according to "
+ "given network (name or ID)")
+ )
+ parser.add_argument(
+ '--local-port',
+ metavar='<local_port>',
+ help=_("List Local IP(s) according to "
+ "given port (name or ID)")
+ )
+ parser.add_argument(
+ '--local-ip-address',
+ metavar='<local_ip_address>',
+ help=_("List Local IP(s) according to "
+ "given Local IP Address")
+ )
+ parser.add_argument(
+ '--ip-mode',
+ metavar='<ip_mode>',
+ help=_("List Local IP(s) according to "
+ "given IP mode")
+ )
+
+ identity_common.add_project_domain_option_to_parser(parser)
+
+ return parser
+
+ def take_action(self, parsed_args):
+ client = self.app.client_manager.network
+ columns = (
+ 'id',
+ 'name',
+ 'description',
+ 'project_id',
+ 'local_port_id',
+ 'network_id',
+ 'local_ip_address',
+ 'ip_mode',
+ )
+ column_headers = (
+ 'ID',
+ 'Name',
+ 'Description',
+ 'Project',
+ 'Local Port ID',
+ 'Network',
+ 'Local IP address',
+ 'IP mode',
+ )
+ attrs = {}
+ if parsed_args.name:
+ attrs['name'] = parsed_args.name
+ if 'project' in parsed_args and parsed_args.project is not None:
+ identity_client = self.app.client_manager.identity
+ project_id = identity_common.find_project(
+ identity_client,
+ parsed_args.project,
+ parsed_args.project_domain,
+ ).id
+ attrs['project_id'] = project_id
+ if parsed_args.network is not None:
+ network = client.find_network(parsed_args.network,
+ ignore_missing=False)
+ attrs['network_id'] = network.id
+ if parsed_args.local_port:
+ port = client.find_port(parsed_args.local_port,
+ ignore_missing=False)
+ attrs['local_port_id'] = port.id
+ if parsed_args.local_ip_address:
+ attrs['local_ip_address'] = parsed_args.local_ip_address
+ if parsed_args.ip_mode:
+ attrs['ip_mode'] = parsed_args.ip_mode
+ data = client.local_ips(**attrs)
+
+ return (column_headers,
+ (utils.get_item_properties(s,
+ columns,
+ formatters={},) for s in data))
+
+
+class ShowLocalIP(command.ShowOne):
+ _description = _("Display local IP details")
+
+ def get_parser(self, prog_name):
+ parser = super().get_parser(prog_name)
+ parser.add_argument(
+ 'local_ip',
+ metavar="<local-ip>",
+ help=_("Local IP to display (name or ID)")
+ )
+
+ return parser
+
+ def take_action(self, parsed_args):
+ client = self.app.client_manager.network
+ obj = client.find_local_ip(
+ parsed_args.local_ip,
+ ignore_missing=False)
+ display_columns, columns = _get_columns(obj)
+ data = utils.get_item_properties(obj, columns, formatters={})
+
+ return (display_columns, data)
diff --git a/openstackclient/network/v2/local_ip_association.py b/openstackclient/network/v2/local_ip_association.py
new file mode 100644
index 00000000..aa0747c5
--- /dev/null
+++ b/openstackclient/network/v2/local_ip_association.py
@@ -0,0 +1,197 @@
+# Copyright 2021 Huawei, Inc. 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.
+#
+
+"""Node Local IP action implementations"""
+
+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
+
+LOG = logging.getLogger(__name__)
+
+
+def _get_columns(item):
+ column_map = {}
+ hidden_columns = ['location', 'name', 'id']
+ return utils.get_osc_show_columns_for_sdk_resource(
+ item, column_map, hidden_columns)
+
+
+class CreateLocalIPAssociation(command.ShowOne):
+ _description = _("Create Local IP Association")
+
+ def get_parser(self, prog_name):
+ parser = super().get_parser(prog_name)
+ parser.add_argument(
+ 'local_ip',
+ metavar='<local-ip>',
+ help=_("Local IP that the port association belongs to "
+ "(IP address or ID)")
+ )
+ parser.add_argument(
+ 'fixed_port',
+ metavar='<fixed_port>',
+ help=_("The ID or Name of Port to allocate Local IP Association")
+ )
+ parser.add_argument(
+ '--fixed-ip',
+ metavar='<fixed_ip>',
+ help=_("Fixed IP for Local IP Association")
+ )
+
+ identity_common.add_project_domain_option_to_parser(parser)
+
+ return parser
+
+ def take_action(self, parsed_args):
+ client = self.app.client_manager.network
+
+ attrs = {}
+ port = client.find_port(parsed_args.fixed_port,
+ ignore_missing=False)
+ attrs['fixed_port_id'] = port.id
+ if parsed_args.fixed_ip:
+ attrs['fixed_ip'] = parsed_args.fixed_ip
+ local_ip = client.find_local_ip(
+ parsed_args.local_ip,
+ ignore_missing=False,
+ )
+ obj = client.create_local_ip_association(local_ip.id, **attrs)
+ display_columns, columns = _get_columns(obj)
+ data = utils.get_item_properties(obj, columns, formatters={})
+
+ return (display_columns, data)
+
+
+class DeleteLocalIPAssociation(command.Command):
+ _description = _("Delete Local IP association(s)")
+
+ def get_parser(self, prog_name):
+ parser = super().get_parser(prog_name)
+ parser.add_argument(
+ 'local_ip',
+ metavar="<local_ip>",
+ help=_("Local IP that the port association belongs to "
+ "(IP address or ID)")
+ )
+ parser.add_argument(
+ 'fixed_port_id',
+ nargs="+",
+ metavar="<fixed_port_id>",
+ help=_("The fixed port ID of Local IP Association")
+ )
+ return parser
+
+ def take_action(self, parsed_args):
+ client = self.app.client_manager.network
+ local_ip = client.find_local_ip(
+ parsed_args.local_ip,
+ ignore_missing=False,
+ )
+ result = 0
+
+ for fixed_port_id in parsed_args.fixed_port_id:
+ try:
+ client.delete_local_ip_association(
+ local_ip.id,
+ fixed_port_id,
+ ignore_missing=False,
+ )
+ except Exception as e:
+ result += 1
+ LOG.error(_("Failed to delete Local IP Association with "
+ "fixed port "
+ "name or ID '%(fixed_port_id)s': %(e)s"),
+ {'fixed port ID': fixed_port_id, 'e': e})
+
+ if result > 0:
+ total = len(parsed_args.fixed_port_id)
+ msg = (_("%(result)s of %(total)s Local IP Associations failed "
+ "to delete.") % {'result': result, 'total': total})
+ raise exceptions.CommandError(msg)
+
+
+class ListLocalIPAssociation(command.Lister):
+ _description = _("List Local IP Associations")
+
+ def get_parser(self, prog_name):
+ parser = super().get_parser(prog_name)
+
+ parser.add_argument(
+ 'local_ip',
+ metavar='<local_ip>',
+ help=_("Local IP that port associations belongs to")
+ )
+ parser.add_argument(
+ '--fixed-port',
+ metavar='<fixed_port>',
+ help=_("Filter the list result by the ID or name of "
+ "the fixed port")
+ )
+ parser.add_argument(
+ '--fixed-ip',
+ metavar='<fixed_ip>',
+ help=_("Filter the list result by fixed ip")
+ )
+ parser.add_argument(
+ '--host',
+ metavar='<host>',
+ help=_("Filter the list result by given host")
+ )
+ identity_common.add_project_domain_option_to_parser(parser)
+
+ return parser
+
+ def take_action(self, parsed_args):
+ client = self.app.client_manager.network
+ columns = (
+ 'local_ip_id',
+ 'local_ip_address',
+ 'fixed_port_id',
+ 'fixed_ip',
+ 'host',
+ )
+ column_headers = (
+ 'Local IP ID',
+ 'Local IP Address',
+ 'Fixed port ID',
+ 'Fixed IP',
+ 'Host'
+ )
+ attrs = {}
+ obj = client.find_local_ip(
+ parsed_args.local_ip,
+ ignore_missing=False,
+ )
+ if parsed_args.fixed_port:
+ port = client.find_port(parsed_args.fixed_port,
+ ignore_missing=False)
+ attrs['fixed_port_id'] = port.id
+ if parsed_args.fixed_ip:
+ attrs['fixed_ip'] = parsed_args.fixed_ip
+ if parsed_args.host:
+ attrs['host'] = parsed_args.host
+
+ data = client.local_ip_associations(obj, **attrs)
+
+ return (column_headers,
+ (utils.get_item_properties(s,
+ columns,
+ formatters={}) for s in data))
diff --git a/openstackclient/network/v2/network.py b/openstackclient/network/v2/network.py
index 191e4aa8..a239e0fe 100644
--- a/openstackclient/network/v2/network.py
+++ b/openstackclient/network/v2/network.py
@@ -58,16 +58,18 @@ def _get_columns_network(item):
'is_shared': 'shared',
'ipv4_address_scope_id': 'ipv4_address_scope',
'ipv6_address_scope_id': 'ipv6_address_scope',
- 'tenant_id': 'project_id',
'tags': 'tags',
}
- return utils.get_osc_show_columns_for_sdk_resource(item, column_map)
+ hidden_columns = ['location']
+ return utils.get_osc_show_columns_for_sdk_resource(
+ item,
+ column_map,
+ hidden_columns
+ )
def _get_columns_compute(item):
- column_map = {
- 'tenant_id': 'project_id',
- }
+ column_map = {}
return utils.get_osc_show_columns_for_sdk_resource(item, column_map)
@@ -96,8 +98,6 @@ def _get_attrs_network(client_manager, parsed_args):
parsed_args.project,
parsed_args.project_domain,
).id
- # TODO(dtroyer): Remove tenant_id when we clean up the SDK refactor
- attrs['tenant_id'] = project_id
attrs['project_id'] = project_id
# "network set" command doesn't support setting availability zone hints.
@@ -568,7 +568,6 @@ class ListNetwork(common.NetworkAndComputeLister):
parsed_args.project,
parsed_args.project_domain,
)
- args['tenant_id'] = project.id
args['project_id'] = project.id
if parsed_args.share:
diff --git a/openstackclient/network/v2/network_agent.py b/openstackclient/network/v2/network_agent.py
index c995e36c..3024d026 100644
--- a/openstackclient/network/v2/network_agent.py
+++ b/openstackclient/network/v2/network_agent.py
@@ -50,7 +50,12 @@ def _get_network_columns(item):
'is_admin_state_up': 'admin_state_up',
'is_alive': 'alive',
}
- return utils.get_osc_show_columns_for_sdk_resource(item, column_map)
+ hidden_columns = ['location']
+ return utils.get_osc_show_columns_for_sdk_resource(
+ item,
+ column_map,
+ hidden_columns
+ )
class AddNetworkToAgent(command.Command):
diff --git a/openstackclient/network/v2/network_auto_allocated_topology.py b/openstackclient/network/v2/network_auto_allocated_topology.py
index 7b7df4d7..496606ba 100644
--- a/openstackclient/network/v2/network_auto_allocated_topology.py
+++ b/openstackclient/network/v2/network_auto_allocated_topology.py
@@ -25,10 +25,13 @@ LOG = logging.getLogger(__name__)
def _get_columns(item):
- column_map = {
- 'tenant_id': 'project_id',
- }
- return utils.get_osc_show_columns_for_sdk_resource(item, column_map)
+ column_map = {}
+ hidden_columns = ['location']
+ return utils.get_osc_show_columns_for_sdk_resource(
+ item,
+ column_map,
+ hidden_columns
+ )
def _format_check_resource_columns():
@@ -51,7 +54,7 @@ def _get_attrs(client_manager, parsed_args):
parsed_args.project,
parsed_args.project_domain,
).id
- attrs['tenant_id'] = project_id
+ attrs['project_id'] = project_id
if parsed_args.check_resources:
attrs['check_resources'] = True
diff --git a/openstackclient/network/v2/network_flavor.py b/openstackclient/network/v2/network_flavor.py
index 6e3a5a04..862155ce 100644
--- a/openstackclient/network/v2/network_flavor.py
+++ b/openstackclient/network/v2/network_flavor.py
@@ -29,10 +29,14 @@ LOG = logging.getLogger(__name__)
def _get_columns(item):
column_map = {
'is_enabled': 'enabled',
- 'tenant_id': 'project_id',
}
- return utils.get_osc_show_columns_for_sdk_resource(item, column_map)
+ hidden_columns = ['location']
+ return utils.get_osc_show_columns_for_sdk_resource(
+ item,
+ column_map,
+ hidden_columns
+ )
def _get_attrs(client_manager, parsed_args):
@@ -52,7 +56,7 @@ def _get_attrs(client_manager, parsed_args):
parsed_args.project,
parsed_args.project_domain,
).id
- attrs['tenant_id'] = project_id
+ attrs['project_id'] = project_id
return attrs
diff --git a/openstackclient/network/v2/network_flavor_profile.py b/openstackclient/network/v2/network_flavor_profile.py
index df7cfb74..719f955c 100644
--- a/openstackclient/network/v2/network_flavor_profile.py
+++ b/openstackclient/network/v2/network_flavor_profile.py
@@ -27,10 +27,14 @@ LOG = logging.getLogger(__name__)
def _get_columns(item):
column_map = {
'is_enabled': 'enabled',
- 'tenant_id': 'project_id',
}
- return utils.get_osc_show_columns_for_sdk_resource(item, column_map)
+ hidden_columns = ['location']
+ return utils.get_osc_show_columns_for_sdk_resource(
+ item,
+ column_map,
+ hidden_columns
+ )
def _get_attrs(client_manager, parsed_args):
@@ -52,7 +56,7 @@ def _get_attrs(client_manager, parsed_args):
parsed_args.project,
parsed_args.project_domain,
).id
- attrs['tenant_id'] = project_id
+ attrs['project_id'] = project_id
return attrs
diff --git a/openstackclient/network/v2/network_meter.py b/openstackclient/network/v2/network_meter.py
index 8b63de2c..b7b77fb1 100644
--- a/openstackclient/network/v2/network_meter.py
+++ b/openstackclient/network/v2/network_meter.py
@@ -29,9 +29,13 @@ LOG = logging.getLogger(__name__)
def _get_columns(item):
column_map = {
'is_shared': 'shared',
- 'tenant_id': 'project_id',
}
- return utils.get_osc_show_columns_for_sdk_resource(item, column_map)
+ hidden_columns = ['location']
+ return utils.get_osc_show_columns_for_sdk_resource(
+ item,
+ column_map,
+ hidden_columns
+ )
def _get_attrs(client_manager, parsed_args):
@@ -46,7 +50,7 @@ def _get_attrs(client_manager, parsed_args):
parsed_args.project,
parsed_args.project_domain,
).id
- attrs['tenant_id'] = project_id
+ attrs['project_id'] = project_id
if parsed_args.share:
attrs['shared'] = True
if parsed_args.no_share:
diff --git a/openstackclient/network/v2/network_meter_rule.py b/openstackclient/network/v2/network_meter_rule.py
index 4117d043..0f427275 100644
--- a/openstackclient/network/v2/network_meter_rule.py
+++ b/openstackclient/network/v2/network_meter_rule.py
@@ -27,10 +27,13 @@ LOG = logging.getLogger(__name__)
def _get_columns(item):
- column_map = {
- 'tenant_id': 'project_id',
- }
- return utils.get_osc_show_columns_for_sdk_resource(item, column_map)
+ column_map = {}
+ hidden_columns = ['location']
+ return utils.get_osc_show_columns_for_sdk_resource(
+ item,
+ column_map,
+ hidden_columns
+ )
def _get_attrs(client_manager, parsed_args):
@@ -59,7 +62,7 @@ def _get_attrs(client_manager, parsed_args):
parsed_args.project,
parsed_args.project_domain,
).id
- attrs['tenant_id'] = project_id
+ attrs['project_id'] = project_id
return attrs
diff --git a/openstackclient/network/v2/network_qos_policy.py b/openstackclient/network/v2/network_qos_policy.py
index 8d431248..bc257901 100644
--- a/openstackclient/network/v2/network_qos_policy.py
+++ b/openstackclient/network/v2/network_qos_policy.py
@@ -29,9 +29,13 @@ LOG = logging.getLogger(__name__)
def _get_columns(item):
column_map = {
'is_shared': 'shared',
- 'tenant_id': 'project_id',
}
- return utils.get_osc_show_columns_for_sdk_resource(item, column_map)
+ hidden_columns = ['location']
+ return utils.get_osc_show_columns_for_sdk_resource(
+ item,
+ column_map,
+ hidden_columns
+ )
def _get_attrs(client_manager, parsed_args):
@@ -59,7 +63,7 @@ def _get_attrs(client_manager, parsed_args):
parsed_args.project,
parsed_args.project_domain,
).id
- attrs['tenant_id'] = project_id
+ attrs['project_id'] = project_id
return attrs
diff --git a/openstackclient/network/v2/network_qos_rule.py b/openstackclient/network/v2/network_qos_rule.py
index 4bf72d26..a4129b83 100644
--- a/openstackclient/network/v2/network_qos_rule.py
+++ b/openstackclient/network/v2/network_qos_rule.py
@@ -46,10 +46,13 @@ ACTION_SHOW = 'get'
def _get_columns(item):
- column_map = {
- 'tenant_id': 'project_id',
- }
- return utils.get_osc_show_columns_for_sdk_resource(item, column_map)
+ column_map = {}
+ hidden_columns = ['location']
+ return utils.get_osc_show_columns_for_sdk_resource(
+ item,
+ column_map,
+ hidden_columns
+ )
def _check_type_parameters(attrs, type, is_create):
diff --git a/openstackclient/network/v2/network_qos_rule_type.py b/openstackclient/network/v2/network_qos_rule_type.py
index 036b682f..1bcfda82 100644
--- a/openstackclient/network/v2/network_qos_rule_type.py
+++ b/openstackclient/network/v2/network_qos_rule_type.py
@@ -24,7 +24,7 @@ def _get_columns(item):
"type": "rule_type_name",
"drivers": "drivers",
}
- invisible_columns = ["id", "name"]
+ invisible_columns = ["id", "location", "name"]
return utils.get_osc_show_columns_for_sdk_resource(
item, column_map, invisible_columns)
diff --git a/openstackclient/network/v2/network_rbac.py b/openstackclient/network/v2/network_rbac.py
index edca872c..00667395 100644
--- a/openstackclient/network/v2/network_rbac.py
+++ b/openstackclient/network/v2/network_rbac.py
@@ -29,9 +29,13 @@ LOG = logging.getLogger(__name__)
def _get_columns(item):
column_map = {
'target_tenant': 'target_project_id',
- 'tenant_id': 'project_id',
}
- return utils.get_osc_show_columns_for_sdk_resource(item, column_map)
+ hidden_columns = ['location']
+ return utils.get_osc_show_columns_for_sdk_resource(
+ item,
+ column_map,
+ hidden_columns
+ )
def _get_attrs(client_manager, parsed_args):
@@ -82,7 +86,7 @@ def _get_attrs(client_manager, parsed_args):
parsed_args.project,
parsed_args.project_domain,
).id
- attrs['tenant_id'] = project_id
+ attrs['project_id'] = project_id
return attrs
diff --git a/openstackclient/network/v2/network_segment.py b/openstackclient/network/v2/network_segment.py
index e18ac475..0f64bd86 100644
--- a/openstackclient/network/v2/network_segment.py
+++ b/openstackclient/network/v2/network_segment.py
@@ -26,7 +26,13 @@ LOG = logging.getLogger(__name__)
def _get_columns(item):
- return utils.get_osc_show_columns_for_sdk_resource(item, {})
+ column_map = {}
+ hidden_columns = ['location']
+ return utils.get_osc_show_columns_for_sdk_resource(
+ item,
+ column_map,
+ hidden_columns
+ )
class CreateNetworkSegment(command.ShowOne,
diff --git a/openstackclient/network/v2/network_segment_range.py b/openstackclient/network/v2/network_segment_range.py
index e105111d..a95adb0a 100644
--- a/openstackclient/network/v2/network_segment_range.py
+++ b/openstackclient/network/v2/network_segment_range.py
@@ -32,7 +32,13 @@ LOG = logging.getLogger(__name__)
def _get_columns(item):
- return utils.get_osc_show_columns_for_sdk_resource(item, {})
+ column_map = {}
+ hidden_columns = ['location']
+ return utils.get_osc_show_columns_for_sdk_resource(
+ item,
+ column_map,
+ hidden_columns
+ )
def _get_ranges(item):
diff --git a/openstackclient/network/v2/port.py b/openstackclient/network/v2/port.py
index 132c384a..b55e729f 100644
--- a/openstackclient/network/v2/port.py
+++ b/openstackclient/network/v2/port.py
@@ -63,9 +63,13 @@ def _get_columns(item):
'binding:vnic_type': 'binding_vnic_type',
'is_admin_state_up': 'admin_state_up',
'is_port_security_enabled': 'port_security_enabled',
- 'tenant_id': 'project_id',
}
- return utils.get_osc_show_columns_for_sdk_resource(item, column_map)
+ hidden_columns = ['location']
+ return utils.get_osc_show_columns_for_sdk_resource(
+ item,
+ column_map,
+ hidden_columns
+ )
class JSONKeyValueAction(argparse.Action):
@@ -134,7 +138,7 @@ def _get_attrs(client_manager, parsed_args):
parsed_args.project,
parsed_args.project_domain,
).id
- attrs['tenant_id'] = project_id
+ attrs['project_id'] = project_id
if parsed_args.disable_port_security:
attrs['port_security_enabled'] = False
@@ -610,6 +614,13 @@ class ListPort(command.Lister):
metavar='<name>',
help=_("List ports according to their name")
)
+ parser.add_argument(
+ '--security-group',
+ action='append',
+ dest='security_groups',
+ metavar='<security-group>',
+ help=_("List only ports associated with this security group")
+ )
identity_common.add_project_domain_option_to_parser(parser)
parser.add_argument(
'--fixed-ip',
@@ -675,13 +686,14 @@ class ListPort(command.Lister):
parsed_args.project,
parsed_args.project_domain,
).id
- filters['tenant_id'] = project_id
filters['project_id'] = project_id
if parsed_args.name:
filters['name'] = parsed_args.name
if parsed_args.fixed_ip:
filters['fixed_ips'] = _prepare_filter_fixed_ips(
self.app.client_manager, parsed_args)
+ if parsed_args.security_groups:
+ filters['security_groups'] = parsed_args.security_groups
_tag.get_tag_filtering_args(parsed_args, filters)
@@ -969,6 +981,12 @@ class UnsetPort(common.NeutronUnsetCommandWithExtraArgs):
action='store_true',
help=_("Clear existing NUMA affinity policy")
)
+ parser.add_argument(
+ '--host',
+ action='store_true',
+ default=False,
+ help=_("Clear host binding for the port.")
+ )
_tag.add_tag_option_to_parser_for_unset(parser, _('port'))
@@ -1026,6 +1044,8 @@ class UnsetPort(common.NeutronUnsetCommandWithExtraArgs):
attrs['data_plane_status'] = None
if parsed_args.numa_policy:
attrs['numa_affinity_policy'] = None
+ if parsed_args.host:
+ attrs['binding:host_id'] = None
attrs.update(
self._parse_extra_properties(parsed_args.extra_properties))
diff --git a/openstackclient/network/v2/router.py b/openstackclient/network/v2/router.py
index dde4eda9..f1fce298 100644
--- a/openstackclient/network/v2/router.py
+++ b/openstackclient/network/v2/router.py
@@ -67,14 +67,13 @@ _formatters = {
def _get_columns(item):
column_map = {
- 'tenant_id': 'project_id',
'is_ha': 'ha',
'is_distributed': 'distributed',
'is_admin_state_up': 'admin_state_up',
}
if hasattr(item, 'interfaces_info'):
column_map['interfaces_info'] = 'interfaces_info'
- invisible_columns = []
+ invisible_columns = ['location']
if item.is_ha is None:
invisible_columns.append('is_ha')
column_map.pop('is_ha')
@@ -110,7 +109,31 @@ def _get_attrs(client_manager, parsed_args):
parsed_args.project,
parsed_args.project_domain,
).id
- attrs['tenant_id'] = project_id
+ attrs['project_id'] = project_id
+ if parsed_args.external_gateway:
+ gateway_info = {}
+ n_client = client_manager.network
+ network = n_client.find_network(
+ parsed_args.external_gateway, ignore_missing=False)
+ gateway_info['network_id'] = network.id
+ if parsed_args.disable_snat:
+ gateway_info['enable_snat'] = False
+ if parsed_args.enable_snat:
+ gateway_info['enable_snat'] = True
+ if parsed_args.fixed_ip:
+ ips = []
+ for ip_spec in parsed_args.fixed_ip:
+ if ip_spec.get('subnet', False):
+ subnet_name_id = ip_spec.pop('subnet')
+ if subnet_name_id:
+ subnet = n_client.find_subnet(subnet_name_id,
+ ignore_missing=False)
+ ip_spec['subnet_id'] = subnet.id
+ if ip_spec.get('ip-address', False):
+ ip_spec['ip_address'] = ip_spec.pop('ip-address')
+ ips.append(ip_spec)
+ gateway_info['external_fixed_ips'] = ips
+ attrs['external_gateway_info'] = gateway_info
return attrs
@@ -320,6 +343,32 @@ class CreateRouter(command.ShowOne, common.NeutronCommandWithExtraArgs):
"repeat option to set multiple availability zones)")
)
_tag.add_tag_option_to_parser_for_create(parser, _('router'))
+ parser.add_argument(
+ '--external-gateway',
+ metavar="<network>",
+ help=_("External Network used as router's gateway (name or ID)")
+ )
+ parser.add_argument(
+ '--fixed-ip',
+ metavar='subnet=<subnet>,ip-address=<ip-address>',
+ action=parseractions.MultiKeyValueAction,
+ optional_keys=['subnet', 'ip-address'],
+ help=_("Desired IP and/or subnet (name or ID) "
+ "on external gateway: "
+ "subnet=<subnet>,ip-address=<ip-address> "
+ "(repeat option to set multiple fixed IP addresses)")
+ )
+ snat_group = parser.add_mutually_exclusive_group()
+ snat_group.add_argument(
+ '--enable-snat',
+ action='store_true',
+ help=_("Enable Source NAT on external gateway")
+ )
+ snat_group.add_argument(
+ '--disable-snat',
+ action='store_true',
+ help=_("Disable Source NAT on external gateway")
+ )
return parser
@@ -338,6 +387,12 @@ class CreateRouter(command.ShowOne, common.NeutronCommandWithExtraArgs):
# tags cannot be set when created, so tags need to be set later.
_tag.update_tags_for_set(client, obj, parsed_args)
+ if (parsed_args.disable_snat or parsed_args.enable_snat or
+ parsed_args.fixed_ip) and not parsed_args.external_gateway:
+ msg = (_("You must specify '--external-gateway' in order "
+ "to specify SNAT or fixed-ip values"))
+ raise exceptions.CommandError(msg)
+
display_columns, columns = _get_columns(obj)
data = utils.get_item_properties(obj, columns, formatters=_formatters)
@@ -459,7 +514,6 @@ class ListRouter(command.Lister):
parsed_args.project,
parsed_args.project_domain,
).id
- args['tenant_id'] = project_id
args['project_id'] = project_id
_tag.get_tag_filtering_args(parsed_args, args)
@@ -725,29 +779,6 @@ class SetRouter(common.NeutronCommandWithExtraArgs):
msg = (_("You must specify '--external-gateway' in order "
"to update the SNAT or fixed-ip values"))
raise exceptions.CommandError(msg)
- if parsed_args.external_gateway:
- gateway_info = {}
- network = client.find_network(
- parsed_args.external_gateway, ignore_missing=False)
- gateway_info['network_id'] = network.id
- if parsed_args.disable_snat:
- gateway_info['enable_snat'] = False
- if parsed_args.enable_snat:
- gateway_info['enable_snat'] = True
- if parsed_args.fixed_ip:
- ips = []
- for ip_spec in parsed_args.fixed_ip:
- if ip_spec.get('subnet', False):
- subnet_name_id = ip_spec.pop('subnet')
- if subnet_name_id:
- subnet = client.find_subnet(subnet_name_id,
- ignore_missing=False)
- ip_spec['subnet_id'] = subnet.id
- if ip_spec.get('ip-address', False):
- ip_spec['ip_address'] = ip_spec.pop('ip-address')
- ips.append(ip_spec)
- gateway_info['external_fixed_ips'] = ips
- attrs['external_gateway_info'] = gateway_info
if ((parsed_args.qos_policy or parsed_args.no_qos_policy) and
not parsed_args.external_gateway):
diff --git a/openstackclient/network/v2/security_group.py b/openstackclient/network/v2/security_group.py
index 37d2dc5b..d8c38f45 100644
--- a/openstackclient/network/v2/security_group.py
+++ b/openstackclient/network/v2/security_group.py
@@ -35,7 +35,6 @@ def _format_network_security_group_rules(sg_rules):
for key in empty_keys:
sg_rule.pop(key)
sg_rule.pop('security_group_id', None)
- sg_rule.pop('tenant_id', None)
sg_rule.pop('project_id', None)
return utils.format_list_of_dicts(sg_rules)
@@ -85,11 +84,17 @@ _formatters_compute = {
def _get_columns(item):
+ # We still support Nova managed security groups, where we have tenant_id.
column_map = {
'security_group_rules': 'rules',
'tenant_id': 'project_id',
}
- return utils.get_osc_show_columns_for_sdk_resource(item, column_map)
+ hidden_columns = ['location']
+ return utils.get_osc_show_columns_for_sdk_resource(
+ item,
+ column_map,
+ hidden_columns
+ )
# TODO(abhiraut): Use the SDK resource mapped attribute names once the
@@ -159,7 +164,7 @@ class CreateSecurityGroup(common.NetworkAndComputeShowOne,
parsed_args.project,
parsed_args.project_domain,
).id
- attrs['tenant_id'] = project_id
+ attrs['project_id'] = project_id
attrs.update(
self._parse_extra_properties(parsed_args.extra_properties))
@@ -264,7 +269,6 @@ class ListSecurityGroup(common.NetworkAndComputeLister):
parsed_args.project,
parsed_args.project_domain,
).id
- filters['tenant_id'] = project_id
filters['project_id'] = project_id
_tag.get_tag_filtering_args(parsed_args, filters)
diff --git a/openstackclient/network/v2/security_group_rule.py b/openstackclient/network/v2/security_group_rule.py
index 252dcb05..a1122616 100644
--- a/openstackclient/network/v2/security_group_rule.py
+++ b/openstackclient/network/v2/security_group_rule.py
@@ -71,10 +71,13 @@ def _format_remote_ip_prefix(rule):
def _get_columns(item):
- column_map = {
- 'tenant_id': 'project_id',
- }
- return utils.get_osc_show_columns_for_sdk_resource(item, column_map)
+ column_map = {}
+ hidden_columns = ['location']
+ return utils.get_osc_show_columns_for_sdk_resource(
+ item,
+ column_map,
+ hidden_columns
+ )
def _convert_to_lowercase(string):
@@ -352,7 +355,7 @@ class CreateSecurityGroupRule(common.NetworkAndComputeShowOne,
parsed_args.project,
parsed_args.project_domain,
).id
- attrs['tenant_id'] = project_id
+ attrs['project_id'] = project_id
attrs.update(
self._parse_extra_properties(parsed_args.extra_properties))
diff --git a/openstackclient/network/v2/subnet.py b/openstackclient/network/v2/subnet.py
index c07fab41..bf6a46d4 100644
--- a/openstackclient/network/v2/subnet.py
+++ b/openstackclient/network/v2/subnet.py
@@ -137,14 +137,17 @@ def _get_columns(item):
column_map = {
'is_dhcp_enabled': 'enable_dhcp',
'subnet_pool_id': 'subnetpool_id',
- 'tenant_id': 'project_id',
}
# Do not show this column when displaying a subnet
- invisible_columns = ['use_default_subnet_pool', 'prefix_length']
+ invisible_columns = [
+ 'location',
+ 'use_default_subnet_pool',
+ 'prefix_length'
+ ]
return utils.get_osc_show_columns_for_sdk_resource(
item,
column_map,
- invisible_columns=invisible_columns
+ invisible_columns
)
@@ -184,7 +187,7 @@ def _get_attrs(client_manager, parsed_args, is_create=True):
parsed_args.project,
parsed_args.project_domain,
).id
- attrs['tenant_id'] = project_id
+ attrs['project_id'] = project_id
attrs['network_id'] = client.find_network(parsed_args.network,
ignore_missing=False).id
if parsed_args.subnet_pool is not None:
@@ -489,6 +492,12 @@ class ListSubnet(command.Lister):
"(in CIDR notation) in output "
"e.g.: --subnet-range 10.10.0.0/16")
)
+ parser.add_argument(
+ '--subnet-pool',
+ metavar='<subnet-pool>',
+ help=_("List only subnets which belong to a given subnet pool "
+ "in output (Name or ID)")
+ )
_tag.add_tag_filtering_option_to_parser(parser, _('subnets'))
return parser
@@ -512,7 +521,6 @@ class ListSubnet(command.Lister):
parsed_args.project,
parsed_args.project_domain,
).id
- filters['tenant_id'] = project_id
filters['project_id'] = project_id
if parsed_args.network:
network_id = network_client.find_network(parsed_args.network,
@@ -524,6 +532,10 @@ class ListSubnet(command.Lister):
filters['name'] = parsed_args.name
if parsed_args.subnet_range:
filters['cidr'] = parsed_args.subnet_range
+ if parsed_args.subnet_pool:
+ subnetpool_id = network_client.find_subnet_pool(
+ parsed_args.subnet_pool, ignore_missing=False).id
+ filters['subnetpool_id'] = subnetpool_id
_tag.get_tag_filtering_args(parsed_args, filters)
data = network_client.subnets(**filters)
diff --git a/openstackclient/network/v2/subnet_pool.py b/openstackclient/network/v2/subnet_pool.py
index 6b88888c..b4142f37 100644
--- a/openstackclient/network/v2/subnet_pool.py
+++ b/openstackclient/network/v2/subnet_pool.py
@@ -36,9 +36,13 @@ def _get_columns(item):
'is_shared': 'shared',
'maximum_prefix_length': 'max_prefixlen',
'minimum_prefix_length': 'min_prefixlen',
- 'tenant_id': 'project_id',
}
- return utils.get_osc_show_columns_for_sdk_resource(item, column_map)
+ hidden_columns = ['location']
+ return utils.get_osc_show_columns_for_sdk_resource(
+ item,
+ column_map,
+ hidden_columns
+ )
_formatters = {
@@ -86,7 +90,7 @@ def _get_attrs(client_manager, parsed_args):
parsed_args.project,
parsed_args.project_domain,
).id
- attrs['tenant_id'] = project_id
+ attrs['project_id'] = project_id
if parsed_args.description is not None:
attrs['description'] = parsed_args.description
@@ -324,7 +328,6 @@ class ListSubnetPool(command.Lister):
parsed_args.project,
parsed_args.project_domain,
).id
- filters['tenant_id'] = project_id
filters['project_id'] = project_id
if parsed_args.name is not None:
filters['name'] = parsed_args.name
diff --git a/openstackclient/tests/functional/common/test_quota.py b/openstackclient/tests/functional/common/test_quota.py
index 9c057460..bf67101a 100644
--- a/openstackclient/tests/functional/common/test_quota.py
+++ b/openstackclient/tests/functional/common/test_quota.py
@@ -11,6 +11,9 @@
# under the License.
import json
+import uuid
+
+from tempest.lib import exceptions
from openstackclient.tests.functional import base
@@ -165,3 +168,25 @@ class QuotaTests(base.TestCase):
# returned attributes
self.assertTrue(cmd_output["key-pairs"] >= 0)
self.assertTrue(cmd_output["snapshots"] >= 0)
+
+ def test_quota_network_set_with_check_limit(self):
+ if not self.haz_network:
+ self.skipTest('No Network service present')
+ if not self.is_extension_enabled('quota-check-limit'):
+ self.skipTest('No "quota-check-limit" extension present')
+
+ self.openstack('quota set --networks 40 ' + self.PROJECT_NAME)
+ cmd_output = json.loads(self.openstack(
+ 'quota list -f json --network'
+ ))
+ self.assertIsNotNone(cmd_output)
+ self.assertEqual(40, cmd_output[0]['Networks'])
+
+ # That will ensure we have at least two networks in the system.
+ for _ in range(2):
+ self.openstack('network create --project %s %s' %
+ (self.PROJECT_NAME, uuid.uuid4().hex))
+
+ self.assertRaises(exceptions.CommandFailed, self.openstack,
+ 'quota set --networks 1 --check-limit ' +
+ self.PROJECT_NAME)
diff --git a/openstackclient/tests/functional/compute/v2/test_server.py b/openstackclient/tests/functional/compute/v2/test_server.py
index 59b1fad5..cf4bcbc2 100644
--- a/openstackclient/tests/functional/compute/v2/test_server.py
+++ b/openstackclient/tests/functional/compute/v2/test_server.py
@@ -1072,7 +1072,7 @@ class ServerTests(common.ComputeTestCase):
self.assertNotIn('nics are required after microversion 2.36',
e.stderr)
- def test_server_add_remove_network_port(self):
+ def test_server_add_remove_network(self):
name = uuid.uuid4().hex
cmd_output = json.loads(self.openstack(
'server create -f json ' +
@@ -1085,18 +1085,63 @@ class ServerTests(common.ComputeTestCase):
self.assertIsNotNone(cmd_output['id'])
self.assertEqual(name, cmd_output['name'])
+ self.addCleanup(self.openstack, 'server delete --wait ' + name)
+ # add network and check 'public' is in server show
self.openstack(
'server add network ' + name + ' public')
+ wait_time = 0
+ while wait_time < 60:
+ cmd_output = json.loads(self.openstack(
+ 'server show -f json ' + name
+ ))
+ if 'public' not in cmd_output['addresses']:
+ # Hang out for a bit and try again
+ print('retrying add network check')
+ wait_time += 10
+ time.sleep(10)
+ else:
+ break
+ addresses = cmd_output['addresses']
+ self.assertIn('public', addresses)
+
+ # remove network and check 'public' is not in server show
+ self.openstack('server remove network ' + name + ' public')
+
+ wait_time = 0
+ while wait_time < 60:
+ cmd_output = json.loads(self.openstack(
+ 'server show -f json ' + name
+ ))
+ if 'public' in cmd_output['addresses']:
+ # Hang out for a bit and try again
+ print('retrying remove network check')
+ wait_time += 10
+ time.sleep(10)
+ else:
+ break
+
+ addresses = cmd_output['addresses']
+ self.assertNotIn('public', addresses)
+
+ def test_server_add_remove_port(self):
+ name = uuid.uuid4().hex
cmd_output = json.loads(self.openstack(
- 'server show -f json ' + name
+ 'server create -f json ' +
+ '--network private ' +
+ '--flavor ' + self.flavor_name + ' ' +
+ '--image ' + self.image_name + ' ' +
+ '--wait ' +
+ name
))
- addresses = cmd_output['addresses']
- self.assertIn('public', addresses)
+ self.assertIsNotNone(cmd_output['id'])
+ self.assertEqual(name, cmd_output['name'])
+ self.addCleanup(self.openstack, 'server delete --wait ' + name)
- port_name = 'test-port'
+ # create port, record one of its ip address
+ port_name = uuid.uuid4().hex
cmd_output = json.loads(self.openstack(
'port list -f json'
@@ -1108,14 +1153,100 @@ class ServerTests(common.ComputeTestCase):
'--network private ' + port_name
))
self.assertIsNotNone(cmd_output['id'])
+ ip_address = cmd_output['fixed_ips'][0]['ip_address']
+ self.addCleanup(self.openstack, 'port delete ' + port_name)
+ # add port to server, assert the ip address of the port appears
self.openstack('server add port ' + name + ' ' + port_name)
+ wait_time = 0
+ while wait_time < 60:
+ cmd_output = json.loads(self.openstack(
+ 'server show -f json ' + name
+ ))
+ if ip_address not in cmd_output['addresses']['private']:
+ # Hang out for a bit and try again
+ print('retrying add port check')
+ wait_time += 10
+ time.sleep(10)
+ else:
+ break
+ addresses = cmd_output['addresses']['private']
+ self.assertIn(ip_address, addresses)
+
+ # remove port, assert the ip address of the port doesn't appear
+ self.openstack('server remove port ' + name + ' ' + port_name)
+
+ wait_time = 0
+ while wait_time < 60:
+ cmd_output = json.loads(self.openstack(
+ 'server show -f json ' + name
+ ))
+ if ip_address in cmd_output['addresses']['private']:
+ # Hang out for a bit and try again
+ print('retrying add port check')
+ wait_time += 10
+ time.sleep(10)
+ else:
+ break
+ addresses = cmd_output['addresses']['private']
+ self.assertNotIn(ip_address, addresses)
+
+ def test_server_add_remove_volume(self):
+ volume_wait_for = volume_common.BaseVolumeTests.wait_for_status
+
+ name = uuid.uuid4().hex
cmd_output = json.loads(self.openstack(
- 'server show -f json ' + name
+ 'server create -f json ' +
+ '--network private ' +
+ '--flavor ' + self.flavor_name + ' ' +
+ '--image ' + self.image_name + ' ' +
+ '--wait ' +
+ name
))
- # TODO(diwei): test remove network/port after the commands are switched
+ self.assertIsNotNone(cmd_output['id'])
+ self.assertEqual(name, cmd_output['name'])
+ self.addCleanup(self.openstack, 'server delete --wait ' + name)
+ server_id = cmd_output['id']
+
+ volume_name = uuid.uuid4().hex
+ cmd_output = json.loads(self.openstack(
+ 'volume create -f json ' +
+ '--size 1 ' +
+ volume_name
+ ))
+
+ self.assertIsNotNone(cmd_output['id'])
+ self.assertEqual(volume_name, cmd_output['name'])
+ volume_wait_for('volume', volume_name, 'available')
+ self.addCleanup(self.openstack, 'volume delete ' + volume_name)
+ volume_id = cmd_output['id']
+
+ cmd_output = json.loads(self.openstack(
+ 'server add volume -f json ' +
+ name + ' ' +
+ volume_name + ' ' +
+ '--tag bar'
+ ))
+
+ self.assertIsNotNone(cmd_output['ID'])
+ self.assertEqual(server_id, cmd_output['Server ID'])
+ self.assertEqual(volume_id, cmd_output['Volume ID'])
+ volume_attachment_id = cmd_output['ID']
+
+ cmd_output = json.loads(self.openstack(
+ 'server volume list -f json ' +
+ name
+ ))
+
+ self.assertEqual(volume_attachment_id, cmd_output[0]['ID'])
+ self.assertEqual(server_id, cmd_output[0]['Server ID'])
+ self.assertEqual(volume_id, cmd_output[0]['Volume ID'])
+
+ volume_wait_for('volume', volume_name, 'in-use')
+ self.openstack('server remove volume ' + name + ' ' + volume_name)
+ volume_wait_for('volume', volume_name, 'available')
- self.openstack('server delete ' + name)
- self.openstack('port delete ' + port_name)
+ raw_output = self.openstack('server volume list ' + name)
+ self.assertEqual('\n', raw_output)
diff --git a/openstackclient/tests/functional/network/v2/test_local_ip.py b/openstackclient/tests/functional/network/v2/test_local_ip.py
new file mode 100644
index 00000000..dd278e38
--- /dev/null
+++ b/openstackclient/tests/functional/network/v2/test_local_ip.py
@@ -0,0 +1,161 @@
+# Copyright 2021 Huawei, Inc. 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 json
+import uuid
+
+from openstackclient.tests.functional.network.v2 import common
+
+
+class LocalIPTests(common.NetworkTests):
+ """Functional tests for local IP"""
+
+ def setUp(self):
+ super(LocalIPTests, self).setUp()
+ # Nothing in this class works with Nova Network
+ if not self.haz_network:
+ self.skipTest("No Network service present")
+ if not self.is_extension_enabled('local-ip'):
+ self.skipTest("No local-ip extension present")
+
+ def test_local_ip_create_and_delete(self):
+ """Test create, delete multiple"""
+ name1 = uuid.uuid4().hex
+ cmd_output = json.loads(self.openstack(
+ 'local ip create -f json ' +
+ name1
+ ))
+ self.assertEqual(
+ name1,
+ cmd_output['name'],
+ )
+
+ name2 = uuid.uuid4().hex
+ cmd_output = json.loads(self.openstack(
+ 'local ip create -f json ' +
+ name2
+ ))
+ self.assertEqual(
+ name2,
+ cmd_output['name'],
+ )
+
+ raw_output = self.openstack(
+ 'local ip delete ' + name1 + ' ' + name2,
+ )
+ self.assertOutput('', raw_output)
+
+ def test_local_ip_list(self):
+ """Test create, list filters, delete"""
+ # Get project IDs
+ cmd_output = json.loads(self.openstack('token issue -f json '))
+ auth_project_id = cmd_output['project_id']
+
+ cmd_output = json.loads(self.openstack('project list -f json '))
+ admin_project_id = None
+ demo_project_id = None
+ for p in cmd_output:
+ if p['Name'] == 'admin':
+ admin_project_id = p['ID']
+ if p['Name'] == 'demo':
+ demo_project_id = p['ID']
+
+ # Verify assumptions:
+ # * admin and demo projects are present
+ # * demo and admin are distinct projects
+ # * tests run as admin
+ self.assertIsNotNone(admin_project_id)
+ self.assertIsNotNone(demo_project_id)
+ self.assertNotEqual(admin_project_id, demo_project_id)
+ self.assertEqual(admin_project_id, auth_project_id)
+
+ name1 = uuid.uuid4().hex
+ cmd_output = json.loads(self.openstack(
+ 'local ip create -f json ' +
+ name1
+ ))
+ self.addCleanup(self.openstack, 'local ip delete ' + name1)
+ self.assertEqual(
+ admin_project_id,
+ cmd_output["project_id"],
+ )
+
+ name2 = uuid.uuid4().hex
+ cmd_output = json.loads(self.openstack(
+ 'local ip create -f json ' +
+ '--project ' + demo_project_id +
+ ' ' + name2
+ ))
+ self.addCleanup(self.openstack, 'local ip delete ' + name2)
+ self.assertEqual(
+ demo_project_id,
+ cmd_output["project_id"],
+ )
+
+ # Test list
+ cmd_output = json.loads(self.openstack(
+ 'local ip list -f json ',
+ ))
+ names = [x["Name"] for x in cmd_output]
+ self.assertIn(name1, names)
+ self.assertIn(name2, names)
+
+ # Test list --project
+ cmd_output = json.loads(self.openstack(
+ 'local ip list -f json ' +
+ '--project ' + demo_project_id
+ ))
+ names = [x["Name"] for x in cmd_output]
+ self.assertNotIn(name1, names)
+ self.assertIn(name2, names)
+
+ # Test list --name
+ cmd_output = json.loads(self.openstack(
+ 'local ip list -f json ' +
+ '--name ' + name1
+ ))
+ names = [x["Name"] for x in cmd_output]
+ self.assertIn(name1, names)
+ self.assertNotIn(name2, names)
+
+ def test_local_ip_set_unset_and_show(self):
+ """Tests create options, set, and show"""
+ name = uuid.uuid4().hex
+ newname = name + "_"
+ cmd_output = json.loads(self.openstack(
+ 'local ip create -f json ' +
+ '--description aaaa ' +
+ name
+ ))
+ self.addCleanup(self.openstack, 'local ip delete ' + newname)
+ self.assertEqual(name, cmd_output['name'])
+ self.assertEqual('aaaa', cmd_output['description'])
+
+ # Test set name and description
+ raw_output = self.openstack(
+ 'local ip set ' +
+ '--name ' + newname + ' ' +
+ '--description bbbb ' +
+ name,
+ )
+ self.assertOutput('', raw_output)
+
+ # Show the updated local ip
+ cmd_output = json.loads(self.openstack(
+ 'local ip show -f json ' +
+ newname,
+ ))
+ self.assertEqual(newname, cmd_output['name'])
+ self.assertEqual('bbbb', cmd_output['description'])
diff --git a/openstackclient/tests/functional/volume/v3/test_volume.py b/openstackclient/tests/functional/volume/v3/test_volume.py
index 6635167d..c1b45e2f 100644
--- a/openstackclient/tests/functional/volume/v3/test_volume.py
+++ b/openstackclient/tests/functional/volume/v3/test_volume.py
@@ -152,6 +152,7 @@ class VolumeTests(common.BaseVolumeTests):
name,
)
self.assertOutput('', raw_output)
+ self.wait_for_status("volume", new_name, "available")
cmd_output = json.loads(self.openstack(
'volume show -f json ' +
diff --git a/openstackclient/tests/unit/common/test_quota.py b/openstackclient/tests/unit/common/test_quota.py
index 8771359c..896a63a7 100644
--- a/openstackclient/tests/unit/common/test_quota.py
+++ b/openstackclient/tests/unit/common/test_quota.py
@@ -950,6 +950,49 @@ class TestQuotaSet(TestQuota):
)
self.assertIsNone(result)
+ def test_quota_set_with_check_limit(self):
+ arglist = [
+ '--subnets', str(network_fakes.QUOTA['subnet']),
+ '--volumes', str(volume_fakes.QUOTA['volumes']),
+ '--cores', str(compute_fakes.core_num),
+ '--check-limit',
+ self.projects[0].name,
+ ]
+ verifylist = [
+ ('subnet', network_fakes.QUOTA['subnet']),
+ ('volumes', volume_fakes.QUOTA['volumes']),
+ ('cores', compute_fakes.core_num),
+ ('check_limit', True),
+ ('project', self.projects[0].name),
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ result = self.cmd.take_action(parsed_args)
+
+ kwargs_compute = {
+ 'cores': compute_fakes.core_num,
+ }
+ kwargs_volume = {
+ 'volumes': volume_fakes.QUOTA['volumes'],
+ }
+ kwargs_network = {
+ 'subnet': network_fakes.QUOTA['subnet'],
+ 'check_limit': True,
+ }
+ self.compute_quotas_mock.update.assert_called_once_with(
+ self.projects[0].id,
+ **kwargs_compute
+ )
+ self.volume_quotas_mock.update.assert_called_once_with(
+ self.projects[0].id,
+ **kwargs_volume
+ )
+ self.network_mock.update_quota.assert_called_once_with(
+ self.projects[0].id,
+ **kwargs_network
+ )
+ self.assertIsNone(result)
+
class TestQuotaShow(TestQuota):
diff --git a/openstackclient/tests/unit/compute/v2/fakes.py b/openstackclient/tests/unit/compute/v2/fakes.py
index 23468ebc..55572cd8 100644
--- a/openstackclient/tests/unit/compute/v2/fakes.py
+++ b/openstackclient/tests/unit/compute/v2/fakes.py
@@ -21,6 +21,9 @@ import uuid
from novaclient import api_versions
from openstack.compute.v2 import flavor as _flavor
from openstack.compute.v2 import server
+from openstack.compute.v2 import server_interface as _server_interface
+from openstack.compute.v2 import service
+from openstack.compute.v2 import volume_attachment
from openstackclient.api import compute_v2
from openstackclient.tests.unit import fakes
@@ -762,7 +765,7 @@ class FakeService(object):
:param dict attrs:
A dictionary with all attributes
:return:
- A FakeResource object, with id, host, binary, and so on
+ A fake Service object, with id, host, binary, and so on
"""
attrs = attrs or {}
@@ -772,21 +775,18 @@ class FakeService(object):
'host': 'host-' + uuid.uuid4().hex,
'binary': 'binary-' + uuid.uuid4().hex,
'status': 'enabled',
- 'zone': 'zone-' + uuid.uuid4().hex,
+ 'availability_zone': 'zone-' + uuid.uuid4().hex,
'state': 'state-' + uuid.uuid4().hex,
'updated_at': 'time-' + uuid.uuid4().hex,
'disabled_reason': 'earthquake',
# Introduced in API microversion 2.11
- 'forced_down': False,
+ 'is_forced_down': False,
}
# Overwrite default attributes.
service_info.update(attrs)
- service = fakes.FakeResource(info=copy.deepcopy(service_info),
- loaded=True)
-
- return service
+ return service.Service(**service_info)
@staticmethod
def create_services(attrs=None, count=2):
@@ -1803,3 +1803,85 @@ class FakeVolumeAttachment(object):
attrs, methods))
return volume_attachments
+
+ @staticmethod
+ def create_one_sdk_volume_attachment(attrs=None, methods=None):
+ """Create a fake sdk VolumeAttachment.
+
+ :param dict attrs:
+ A dictionary with all attributes
+ :param dict methods:
+ A dictionary with all methods
+ :return:
+ A fake VolumeAttachment object, with id, device, and so on
+ """
+ attrs = attrs or {}
+ methods = methods or {}
+
+ # Set default attributes.
+ volume_attachment_info = {
+ "id": uuid.uuid4().hex,
+ "device": "/dev/sdb",
+ "server_id": uuid.uuid4().hex,
+ "volume_id": uuid.uuid4().hex,
+ # introduced in API microversion 2.70
+ "tag": "foo",
+ # introduced in API microversion 2.79
+ "delete_on_termination": True,
+ # introduced in API microversion 2.89
+ "attachment_id": uuid.uuid4().hex,
+ "bdm_uuid": uuid.uuid4().hex
+ }
+
+ # Overwrite default attributes.
+ volume_attachment_info.update(attrs)
+
+ return volume_attachment.VolumeAttachment(**volume_attachment_info)
+
+ @staticmethod
+ def create_sdk_volume_attachments(attrs=None, methods=None, count=2):
+ """Create multiple fake VolumeAttachment objects (BDMs).
+
+ :param dict attrs:
+ A dictionary with all attributes
+ :param dict methods:
+ A dictionary with all methods
+ :param int count:
+ The number of volume attachments to fake
+ :return:
+ A list of VolumeAttachment objects faking the volume attachments.
+ """
+ volume_attachments = []
+ for i in range(0, count):
+ volume_attachments.append(
+ FakeVolumeAttachment.create_one_sdk_volume_attachment(
+ attrs, methods))
+
+ return volume_attachments
+
+
+def create_one_server_interface(attrs=None):
+ """Create a fake SDK ServerInterface.
+
+ :param dict attrs: A dictionary with all attributes
+ :param dict methods: A dictionary with all methods
+ :return: A fake ServerInterface object with various attributes set
+ """
+ attrs = attrs or {}
+
+ # Set default attributes.
+ server_interface_info = {
+ "fixed_ips": uuid.uuid4().hex,
+ "mac_addr": "aa:aa:aa:aa:aa:aa",
+ "net_id": uuid.uuid4().hex,
+ "port_id": uuid.uuid4().hex,
+ "port_state": "ACTIVE",
+ "server_id": uuid.uuid4().hex,
+ # introduced in API microversion 2.70
+ "tag": "foo",
+ }
+
+ # Overwrite default attributes.
+ server_interface_info.update(attrs)
+
+ return _server_interface.ServerInterface(**server_interface_info)
diff --git a/openstackclient/tests/unit/compute/v2/test_aggregate.py b/openstackclient/tests/unit/compute/v2/test_aggregate.py
index 071f2a30..3a7a81cb 100644
--- a/openstackclient/tests/unit/compute/v2/test_aggregate.py
+++ b/openstackclient/tests/unit/compute/v2/test_aggregate.py
@@ -234,6 +234,7 @@ class TestAggregateList(TestAggregate):
"Name",
"Availability Zone",
"Properties",
+ "Hosts",
)
list_data = ((
@@ -251,6 +252,7 @@ class TestAggregateList(TestAggregate):
for key, value in TestAggregate.fake_ag.metadata.items()
if key != 'availability_zone'
}),
+ format_columns.ListColumn(TestAggregate.fake_ag.hosts),
), )
def setUp(self):
diff --git a/openstackclient/tests/unit/compute/v2/test_server.py b/openstackclient/tests/unit/compute/v2/test_server.py
index f7ed4b16..480dcfe1 100644
--- a/openstackclient/tests/unit/compute/v2/test_server.py
+++ b/openstackclient/tests/unit/compute/v2/test_server.py
@@ -105,6 +105,9 @@ class TestServer(compute_fakes.TestComputev2):
self.volumes_mock = self.app.client_manager.volume.volumes
self.volumes_mock.reset_mock()
+ self.app.client_manager.sdk_connection.volume = mock.Mock()
+ self.sdk_volume_client = self.app.client_manager.sdk_connection.volume
+
# Get a shortcut to the volume client VolumeManager Mock
self.snapshots_mock = self.app.client_manager.volume.volume_snapshots
self.snapshots_mock.reset_mock()
@@ -146,13 +149,18 @@ class TestServer(compute_fakes.TestComputev2):
)
# This is the return value for compute_client.find_server()
- self.sdk_client.find_server = compute_fakes.FakeServer.get_servers(
- servers,
- 0,
- )
+ self.sdk_client.find_server.side_effect = servers
return servers
+ def setup_sdk_volumes_mock(self, count):
+ volumes = volume_fakes.FakeVolume.create_sdk_volumes(count=count)
+
+ # This is the return value for volume_client.find_volume()
+ self.sdk_volume_client.find_volume.side_effect = volumes
+
+ return volumes
+
def run_method_with_servers(self, method_name, server_count):
servers = self.setup_servers_mock(server_count)
@@ -184,113 +192,160 @@ class TestServer(compute_fakes.TestComputev2):
method.assert_called_with()
self.assertIsNone(result)
+ def run_method_with_sdk_servers(self, method_name, server_count):
+ servers = self.setup_sdk_servers_mock(count=server_count)
+
+ arglist = [s.id for s in servers]
+ verifylist = [
+ ('server', arglist),
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ result = self.cmd.take_action(parsed_args)
+
+ calls = [call(s.id) for s in servers]
+ method = getattr(self.sdk_client, method_name)
+ method.assert_has_calls(calls)
+ self.assertIsNone(result)
+
class TestServerAddFixedIP(TestServer):
def setUp(self):
- super(TestServerAddFixedIP, self).setUp()
+ super().setUp()
# Get the command object to test
self.cmd = server.AddFixedIP(self.app, None)
- # Set add_fixed_ip method to be tested.
- self.methods = {
- 'interface_attach': None,
- }
+ # Mock network methods
+ self.find_network = mock.Mock()
+ self.app.client_manager.network.find_network = self.find_network
- def _test_server_add_fixed_ip(self, extralist, fixed_ip_address):
- servers = self.setup_servers_mock(count=1)
+ @mock.patch.object(sdk_utils, 'supports_microversion')
+ def test_server_add_fixed_ip_pre_v244(self, sm_mock):
+ sm_mock.return_value = False
+
+ servers = self.setup_sdk_servers_mock(count=1)
network = compute_fakes.FakeNetwork.create_one_network()
- with mock.patch(
- 'openstackclient.api.compute_v2.APIv2.network_find'
- ) as net_mock:
- net_mock.return_value = network
+ with mock.patch.object(
+ self.app.client_manager,
+ 'is_network_endpoint_enabled',
+ return_value=False
+ ):
arglist = [
servers[0].id,
network['id'],
- ] + extralist
+ ]
verifylist = [
('server', servers[0].id),
('network', network['id']),
- ('fixed_ip_address', fixed_ip_address),
+ ('fixed_ip_address', None),
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
result = self.cmd.take_action(parsed_args)
- servers[0].interface_attach.assert_called_once_with(
- port_id=None,
- net_id=network['id'],
- fixed_ip=fixed_ip_address,
+ self.sdk_client.add_fixed_ip_to_server.assert_called_once_with(
+ servers[0].id,
+ network['id']
)
- self.assertIsNone(result)
+ # the legacy API operates asynchronously
+ self.assertEqual(((), ()), result)
- def test_server_add_fixed_ip(self):
- self._test_server_add_fixed_ip([], None)
+ @mock.patch.object(sdk_utils, 'supports_microversion')
+ def test_server_add_fixed_ip_pre_v244_with_fixed_ip(self, sm_mock):
+ sm_mock.return_value = False
- def test_server_add_specific_fixed_ip(self):
- extralist = ['--fixed-ip-address', '5.6.7.8']
- self._test_server_add_fixed_ip(extralist, '5.6.7.8')
+ servers = self.setup_sdk_servers_mock(count=1)
+ network = compute_fakes.FakeNetwork.create_one_network()
- def test_server_add_fixed_ip_with_tag(self):
- self.app.client_manager.compute.api_version = api_versions.APIVersion(
- '2.49')
+ with mock.patch.object(
+ self.app.client_manager,
+ 'is_network_endpoint_enabled',
+ return_value=False
+ ):
+ arglist = [
+ servers[0].id,
+ network['id'],
+ '--fixed-ip-address', '5.6.7.8'
+ ]
+ verifylist = [
+ ('server', servers[0].id),
+ ('network', network['id']),
+ ('fixed_ip_address', '5.6.7.8'),
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
- servers = self.setup_servers_mock(count=1)
+ result = self.cmd.take_action(parsed_args)
+
+ self.sdk_client.add_fixed_ip_to_server.assert_called_once_with(
+ servers[0].id,
+ network['id']
+ )
+ # the legacy API operates asynchronously
+ self.assertEqual(((), ()), result)
+
+ @mock.patch.object(sdk_utils, 'supports_microversion')
+ def test_server_add_fixed_ip_pre_v244_with_tag(self, sm_mock):
+ sm_mock.return_value = False
+
+ servers = self.setup_sdk_servers_mock(count=1)
network = compute_fakes.FakeNetwork.create_one_network()
- with mock.patch(
- 'openstackclient.api.compute_v2.APIv2.network_find'
- ) as net_mock:
- net_mock.return_value = network
+ with mock.patch.object(
+ self.app.client_manager,
+ 'is_network_endpoint_enabled',
+ return_value=False
+ ):
arglist = [
servers[0].id,
network['id'],
'--fixed-ip-address', '5.6.7.8',
- '--tag', 'tag1',
+ '--tag', 'tag1'
]
verifylist = [
('server', servers[0].id),
('network', network['id']),
('fixed_ip_address', '5.6.7.8'),
- ('tag', 'tag1'),
+ ('tag', 'tag1')
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
- result = self.cmd.take_action(parsed_args)
- servers[0].interface_attach.assert_called_once_with(
- port_id=None,
- net_id=network['id'],
- fixed_ip='5.6.7.8',
- tag='tag1'
- )
- self.assertIsNone(result)
+ ex = self.assertRaises(
+ exceptions.CommandError,
+ self.cmd.take_action,
+ parsed_args)
+ self.assertIn(
+ '--os-compute-api-version 2.49 or greater is required',
+ str(ex))
- def test_server_add_fixed_ip_with_tag_pre_v249(self):
- self.app.client_manager.compute.api_version = api_versions.APIVersion(
- '2.48')
+ @mock.patch.object(sdk_utils, 'supports_microversion')
+ def test_server_add_fixed_ip_pre_v249_with_tag(self, sm_mock):
+ sm_mock.side_effect = [False, True]
- servers = self.setup_servers_mock(count=1)
+ servers = self.setup_sdk_servers_mock(count=1)
network = compute_fakes.FakeNetwork.create_one_network()
- with mock.patch(
- 'openstackclient.api.compute_v2.APIv2.network_find'
- ) as net_mock:
- net_mock.return_value = network
+ with mock.patch.object(
+ self.app.client_manager,
+ 'is_network_endpoint_enabled',
+ return_value=False
+ ):
arglist = [
servers[0].id,
network['id'],
'--fixed-ip-address', '5.6.7.8',
- '--tag', 'tag1',
+ '--tag', 'tag1'
]
verifylist = [
('server', servers[0].id),
('network', network['id']),
('fixed_ip_address', '5.6.7.8'),
- ('tag', 'tag1'),
+ ('tag', 'tag1')
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
ex = self.assertRaises(
exceptions.CommandError,
self.cmd.take_action,
@@ -299,6 +354,170 @@ class TestServerAddFixedIP(TestServer):
'--os-compute-api-version 2.49 or greater is required',
str(ex))
+ @mock.patch.object(sdk_utils, 'supports_microversion')
+ def test_server_add_fixed_ip(self, sm_mock):
+ sm_mock.side_effect = [True, False]
+
+ servers = self.setup_sdk_servers_mock(count=1)
+ network = compute_fakes.FakeNetwork.create_one_network()
+ interface = compute_fakes.create_one_server_interface()
+ self.sdk_client.create_server_interface.return_value = interface
+
+ with mock.patch.object(
+ self.app.client_manager,
+ 'is_network_endpoint_enabled',
+ return_value=False
+ ):
+ arglist = [
+ servers[0].id,
+ network['id']
+ ]
+ verifylist = [
+ ('server', servers[0].id),
+ ('network', network['id'])
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ expected_columns = (
+ 'Port ID',
+ 'Server ID',
+ 'Network ID',
+ 'MAC Address',
+ 'Port State',
+ 'Fixed IPs',
+ )
+ expected_data = (
+ interface.port_id,
+ interface.server_id,
+ interface.net_id,
+ interface.mac_addr,
+ interface.port_state,
+ format_columns.ListDictColumn(interface.fixed_ips),
+ )
+
+ columns, data = self.cmd.take_action(parsed_args)
+
+ self.assertEqual(expected_columns, columns)
+ self.assertEqual(expected_data, tuple(data))
+ self.sdk_client.create_server_interface.assert_called_once_with(
+ servers[0].id,
+ net_id=network['id'],
+ fixed_ip=None
+ )
+
+ @mock.patch.object(sdk_utils, 'supports_microversion')
+ def test_server_add_fixed_ip_with_fixed_ip(self, sm_mock):
+ sm_mock.side_effect = [True, True]
+
+ servers = self.setup_sdk_servers_mock(count=1)
+ network = compute_fakes.FakeNetwork.create_one_network()
+ interface = compute_fakes.create_one_server_interface()
+ self.sdk_client.create_server_interface.return_value = interface
+
+ with mock.patch.object(
+ self.app.client_manager,
+ 'is_network_endpoint_enabled',
+ return_value=False
+ ):
+ arglist = [
+ servers[0].id,
+ network['id'],
+ '--fixed-ip-address', '5.6.7.8'
+ ]
+ verifylist = [
+ ('server', servers[0].id),
+ ('network', network['id']),
+ ('fixed_ip_address', '5.6.7.8')
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ expected_columns = (
+ 'Port ID',
+ 'Server ID',
+ 'Network ID',
+ 'MAC Address',
+ 'Port State',
+ 'Fixed IPs',
+ 'Tag',
+ )
+ expected_data = (
+ interface.port_id,
+ interface.server_id,
+ interface.net_id,
+ interface.mac_addr,
+ interface.port_state,
+ format_columns.ListDictColumn(interface.fixed_ips),
+ interface.tag,
+ )
+
+ columns, data = self.cmd.take_action(parsed_args)
+
+ self.assertEqual(expected_columns, columns)
+ self.assertEqual(expected_data, tuple(data))
+ self.sdk_client.create_server_interface.assert_called_once_with(
+ servers[0].id,
+ net_id=network['id'],
+ fixed_ip='5.6.7.8'
+ )
+
+ @mock.patch.object(sdk_utils, 'supports_microversion')
+ def test_server_add_fixed_ip_with_tag(self, sm_mock):
+ sm_mock.side_effect = [True, True, True]
+
+ servers = self.setup_sdk_servers_mock(count=1)
+ network = compute_fakes.FakeNetwork.create_one_network()
+ interface = compute_fakes.create_one_server_interface()
+ self.sdk_client.create_server_interface.return_value = interface
+
+ with mock.patch.object(
+ self.app.client_manager,
+ 'is_network_endpoint_enabled',
+ return_value=False
+ ):
+ arglist = [
+ servers[0].id,
+ network['id'],
+ '--fixed-ip-address', '5.6.7.8',
+ '--tag', 'tag1'
+ ]
+ verifylist = [
+ ('server', servers[0].id),
+ ('network', network['id']),
+ ('fixed_ip_address', '5.6.7.8'),
+ ('tag', 'tag1')
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ expected_columns = (
+ 'Port ID',
+ 'Server ID',
+ 'Network ID',
+ 'MAC Address',
+ 'Port State',
+ 'Fixed IPs',
+ 'Tag',
+ )
+ expected_data = (
+ interface.port_id,
+ interface.server_id,
+ interface.net_id,
+ interface.mac_addr,
+ interface.port_state,
+ format_columns.ListDictColumn(interface.fixed_ips),
+ interface.tag,
+ )
+
+ columns, data = self.cmd.take_action(parsed_args)
+
+ self.assertEqual(expected_columns, columns)
+ self.assertEqual(expected_data, tuple(data))
+ self.sdk_client.create_server_interface.assert_called_once_with(
+ servers[0].id,
+ net_id=network['id'],
+ fixed_ip='5.6.7.8',
+ tag='tag1',
+ )
+
@mock.patch(
'openstackclient.api.compute_v2.APIv2.floating_ip_add'
@@ -680,31 +899,44 @@ class TestServerVolume(TestServer):
def setUp(self):
super(TestServerVolume, self).setUp()
- self.volume = volume_fakes.FakeVolume.create_one_volume()
- self.volumes_mock.get.return_value = self.volume
-
self.methods = {
- 'create_server_volume': None,
+ 'create_volume_attachment': None,
+ }
+
+ self.servers = self.setup_sdk_servers_mock(count=1)
+ self.volumes = self.setup_sdk_volumes_mock(count=1)
+
+ attrs = {
+ 'server_id': self.servers[0].id,
+ 'volume_id': self.volumes[0].id,
}
+ self.volume_attachment = \
+ compute_fakes.FakeVolumeAttachment.\
+ create_one_sdk_volume_attachment(attrs=attrs)
+
+ self.sdk_client.create_volume_attachment.return_value = \
+ self.volume_attachment
+
+
+class TestServerAddVolume(TestServerVolume):
+
+ def setUp(self):
+ super(TestServerAddVolume, self).setUp()
# Get the command object to test
self.cmd = server.AddServerVolume(self.app, None)
- def test_server_add_volume(self):
- servers = self.setup_servers_mock(count=1)
- volume_attachment = \
- compute_fakes.FakeVolumeAttachment.create_one_volume_attachment()
- self.servers_volumes_mock.create_server_volume.return_value = \
- volume_attachment
+ @mock.patch.object(sdk_utils, 'supports_microversion', return_value=False)
+ def test_server_add_volume(self, sm_mock):
arglist = [
'--device', '/dev/sdb',
- servers[0].id,
- self.volume.id,
+ self.servers[0].id,
+ self.volumes[0].id,
]
verifylist = [
- ('server', servers[0].id),
- ('volume', self.volume.id),
+ ('server', self.servers[0].id),
+ ('volume', self.volumes[0].id),
('device', '/dev/sdb'),
]
@@ -712,39 +944,36 @@ class TestServerVolume(TestServer):
expected_columns = ('ID', 'Server ID', 'Volume ID', 'Device')
expected_data = (
- volume_attachment.id,
- volume_attachment.serverId,
- volume_attachment.volumeId,
- volume_attachment.device,
+ self.volume_attachment.id,
+ self.volume_attachment.server_id,
+ self.volume_attachment.volume_id,
+ '/dev/sdb',
)
columns, data = self.cmd.take_action(parsed_args)
- self.servers_volumes_mock.create_server_volume.assert_called_once_with(
- servers[0].id, self.volume.id, device='/dev/sdb')
self.assertEqual(expected_columns, columns)
self.assertEqual(expected_data, data)
+ self.sdk_client.create_volume_attachment.assert_called_once_with(
+ self.servers[0], volumeId=self.volumes[0].id, device='/dev/sdb')
- def test_server_add_volume_with_tag(self):
- # requires API 2.49 or later
- self.app.client_manager.compute.api_version = api_versions.APIVersion(
- '2.49')
-
- servers = self.setup_servers_mock(count=1)
- volume_attachment = \
- compute_fakes.FakeVolumeAttachment.create_one_volume_attachment()
- self.servers_volumes_mock.create_server_volume.return_value = \
- volume_attachment
+ @mock.patch.object(sdk_utils, 'supports_microversion')
+ def test_server_add_volume_with_tag(self, sm_mock):
+ def side_effect(compute_client, version):
+ if version == '2.49':
+ return True
+ return False
+ sm_mock.side_effect = side_effect
arglist = [
'--device', '/dev/sdb',
'--tag', 'foo',
- servers[0].id,
- self.volume.id,
+ self.servers[0].id,
+ self.volumes[0].id,
]
verifylist = [
- ('server', servers[0].id),
- ('volume', self.volume.id),
+ ('server', self.servers[0].id),
+ ('volume', self.volumes[0].id),
('device', '/dev/sdb'),
('tag', 'foo'),
]
@@ -753,33 +982,33 @@ class TestServerVolume(TestServer):
expected_columns = ('ID', 'Server ID', 'Volume ID', 'Device', 'Tag')
expected_data = (
- volume_attachment.id,
- volume_attachment.serverId,
- volume_attachment.volumeId,
- volume_attachment.device,
- volume_attachment.tag,
+ self.volume_attachment.id,
+ self.volume_attachment.server_id,
+ self.volume_attachment.volume_id,
+ self.volume_attachment.device,
+ self.volume_attachment.tag,
)
columns, data = self.cmd.take_action(parsed_args)
- self.servers_volumes_mock.create_server_volume.assert_called_once_with(
- servers[0].id, self.volume.id, device='/dev/sdb', tag='foo')
self.assertEqual(expected_columns, columns)
self.assertEqual(expected_data, data)
+ self.sdk_client.create_volume_attachment.assert_called_once_with(
+ self.servers[0],
+ volumeId=self.volumes[0].id,
+ device='/dev/sdb',
+ tag='foo')
- def test_server_add_volume_with_tag_pre_v249(self):
- self.app.client_manager.compute.api_version = api_versions.APIVersion(
- '2.48')
-
- servers = self.setup_servers_mock(count=1)
+ @mock.patch.object(sdk_utils, 'supports_microversion', return_value=False)
+ def test_server_add_volume_with_tag_pre_v249(self, sm_mock):
arglist = [
- servers[0].id,
- self.volume.id,
+ self.servers[0].id,
+ self.volumes[0].id,
'--tag', 'foo',
]
verifylist = [
- ('server', servers[0].id),
- ('volume', self.volume.id),
+ ('server', self.servers[0].id),
+ ('volume', self.volumes[0].id),
('tag', 'foo'),
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
@@ -792,26 +1021,22 @@ class TestServerVolume(TestServer):
'--os-compute-api-version 2.49 or greater is required',
str(ex))
- def test_server_add_volume_with_enable_delete_on_termination(self):
- self.app.client_manager.compute.api_version = api_versions.APIVersion(
- '2.79')
-
- servers = self.setup_servers_mock(count=1)
- volume_attachment = \
- compute_fakes.FakeVolumeAttachment.create_one_volume_attachment()
- self.servers_volumes_mock.create_server_volume.return_value = \
- volume_attachment
-
+ @mock.patch.object(sdk_utils, 'supports_microversion', return_value=True)
+ def test_server_add_volume_with_enable_delete_on_termination(
+ self,
+ sm_mock,
+ ):
+ self.volume_attachment.delete_on_termination = True
arglist = [
'--enable-delete-on-termination',
'--device', '/dev/sdb',
- servers[0].id,
- self.volume.id,
+ self.servers[0].id,
+ self.volumes[0].id,
]
verifylist = [
- ('server', servers[0].id),
- ('volume', self.volume.id),
+ ('server', self.servers[0].id),
+ ('volume', self.volumes[0].id),
('device', '/dev/sdb'),
('enable_delete_on_termination', True),
]
@@ -826,42 +1051,40 @@ class TestServerVolume(TestServer):
'Delete On Termination',
)
expected_data = (
- volume_attachment.id,
- volume_attachment.serverId,
- volume_attachment.volumeId,
- volume_attachment.device,
- volume_attachment.tag,
- volume_attachment.delete_on_termination,
+ self.volume_attachment.id,
+ self.volume_attachment.server_id,
+ self.volume_attachment.volume_id,
+ self.volume_attachment.device,
+ self.volume_attachment.tag,
+ self.volume_attachment.delete_on_termination,
)
columns, data = self.cmd.take_action(parsed_args)
-
- self.servers_volumes_mock.create_server_volume.assert_called_once_with(
- servers[0].id, self.volume.id,
- device='/dev/sdb', delete_on_termination=True)
self.assertEqual(expected_columns, columns)
self.assertEqual(expected_data, data)
+ self.sdk_client.create_volume_attachment.assert_called_once_with(
+ self.servers[0],
+ volumeId=self.volumes[0].id,
+ device='/dev/sdb',
+ delete_on_termination=True)
- def test_server_add_volume_with_disable_delete_on_termination(self):
- self.app.client_manager.compute.api_version = api_versions.APIVersion(
- '2.79')
-
- servers = self.setup_servers_mock(count=1)
- volume_attachment = \
- compute_fakes.FakeVolumeAttachment.create_one_volume_attachment()
- self.servers_volumes_mock.create_server_volume.return_value = \
- volume_attachment
+ @mock.patch.object(sdk_utils, 'supports_microversion', return_value=True)
+ def test_server_add_volume_with_disable_delete_on_termination(
+ self,
+ sm_mock,
+ ):
+ self.volume_attachment.delete_on_termination = False
arglist = [
'--disable-delete-on-termination',
'--device', '/dev/sdb',
- servers[0].id,
- self.volume.id,
+ self.servers[0].id,
+ self.volumes[0].id,
]
verifylist = [
- ('server', servers[0].id),
- ('volume', self.volume.id),
+ ('server', self.servers[0].id),
+ ('volume', self.volumes[0].id),
('device', '/dev/sdb'),
('disable_delete_on_termination', True),
]
@@ -876,37 +1099,43 @@ class TestServerVolume(TestServer):
'Delete On Termination',
)
expected_data = (
- volume_attachment.id,
- volume_attachment.serverId,
- volume_attachment.volumeId,
- volume_attachment.device,
- volume_attachment.tag,
- volume_attachment.delete_on_termination,
+ self.volume_attachment.id,
+ self.volume_attachment.server_id,
+ self.volume_attachment.volume_id,
+ self.volume_attachment.device,
+ self.volume_attachment.tag,
+ self.volume_attachment.delete_on_termination,
)
columns, data = self.cmd.take_action(parsed_args)
- self.servers_volumes_mock.create_server_volume.assert_called_once_with(
- servers[0].id, self.volume.id,
- device='/dev/sdb', delete_on_termination=False)
self.assertEqual(expected_columns, columns)
self.assertEqual(expected_data, data)
+ self.sdk_client.create_volume_attachment.assert_called_once_with(
+ self.servers[0],
+ volumeId=self.volumes[0].id,
+ device='/dev/sdb',
+ delete_on_termination=False)
+ @mock.patch.object(sdk_utils, 'supports_microversion')
def test_server_add_volume_with_enable_delete_on_termination_pre_v279(
self,
+ sm_mock,
):
- self.app.client_manager.compute.api_version = api_versions.APIVersion(
- '2.78')
+ def side_effect(compute_client, version):
+ if version == '2.79':
+ return False
+ return True
+ sm_mock.side_effect = side_effect
- servers = self.setup_servers_mock(count=1)
arglist = [
- servers[0].id,
- self.volume.id,
+ self.servers[0].id,
+ self.volumes[0].id,
'--enable-delete-on-termination',
]
verifylist = [
- ('server', servers[0].id),
- ('volume', self.volume.id),
+ ('server', self.servers[0].id),
+ ('volume', self.volumes[0].id),
('enable_delete_on_termination', True),
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
@@ -917,21 +1146,25 @@ class TestServerVolume(TestServer):
self.assertIn('--os-compute-api-version 2.79 or greater is required',
str(ex))
+ @mock.patch.object(sdk_utils, 'supports_microversion')
def test_server_add_volume_with_disable_delete_on_termination_pre_v279(
self,
+ sm_mock,
):
- self.app.client_manager.compute.api_version = api_versions.APIVersion(
- '2.78')
+ def side_effect(compute_client, version):
+ if version == '2.79':
+ return False
+ return True
+ sm_mock.side_effect = side_effect
- servers = self.setup_servers_mock(count=1)
arglist = [
- servers[0].id,
- self.volume.id,
+ self.servers[0].id,
+ self.volumes[0].id,
'--disable-delete-on-termination',
]
verifylist = [
- ('server', servers[0].id),
- ('volume', self.volume.id),
+ ('server', self.servers[0].id),
+ ('volume', self.volumes[0].id),
('disable_delete_on_termination', True),
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
@@ -942,24 +1175,22 @@ class TestServerVolume(TestServer):
self.assertIn('--os-compute-api-version 2.79 or greater is required',
str(ex))
+ @mock.patch.object(sdk_utils, 'supports_microversion', return_value=True)
def test_server_add_volume_with_disable_and_enable_delete_on_termination(
self,
+ sm_mock,
):
- self.app.client_manager.compute.api_version = api_versions.APIVersion(
- '2.79')
-
- servers = self.setup_servers_mock(count=1)
arglist = [
'--enable-delete-on-termination',
'--disable-delete-on-termination',
'--device', '/dev/sdb',
- servers[0].id,
- self.volume.id,
+ self.servers[0].id,
+ self.volumes[0].id,
]
verifylist = [
- ('server', servers[0].id),
- ('volume', self.volume.id),
+ ('server', self.servers[0].id),
+ ('volume', self.volumes[0].id),
('device', '/dev/sdb'),
('enable_delete_on_termination', True),
('disable_delete_on_termination', True),
@@ -971,6 +1202,37 @@ class TestServerVolume(TestServer):
'with argument --enable-delete-on-termination', str(ex))
+class TestServerRemoveVolume(TestServerVolume):
+
+ def setUp(self):
+ super(TestServerRemoveVolume, self).setUp()
+
+ # Get the command object to test
+ self.cmd = server.RemoveServerVolume(self.app, None)
+
+ def test_server_remove_volume(self):
+ arglist = [
+ self.servers[0].id,
+ self.volumes[0].id,
+ ]
+
+ verifylist = [
+ ('server', self.servers[0].id),
+ ('volume', self.volumes[0].id),
+ ]
+
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ result = self.cmd.take_action(parsed_args)
+
+ self.assertIsNone(result)
+ self.sdk_client.delete_volume_attachment.assert_called_once_with(
+ self.volumes[0],
+ self.servers[0],
+ ignore_missing=False,
+ )
+
+
class TestServerAddNetwork(TestServer):
def setUp(self):
@@ -4028,7 +4290,7 @@ class TestServerDumpCreate(TestServer):
self.run_method_with_servers('trigger_crash_dump', 3)
-class TestServerList(TestServer):
+class _TestServerList(TestServer):
# Columns to be listed up.
columns = (
@@ -4056,7 +4318,7 @@ class TestServerList(TestServer):
)
def setUp(self):
- super(TestServerList, self).setUp()
+ super(_TestServerList, self).setUp()
self.search_opts = {
'reservation_id': None,
@@ -4095,7 +4357,7 @@ class TestServerList(TestServer):
},
'OS-EXT-AZ:availability_zone': 'availability-zone-xxx',
'OS-EXT-SRV-ATTR:host': 'host-name-xxx',
- 'Metadata': '',
+ 'Metadata': format_columns.DictColumn({}),
}
# The servers to be listed.
@@ -4114,10 +4376,11 @@ class TestServerList(TestServer):
# Get the command object to test
self.cmd = server.ListServer(self.app, None)
- # Prepare data returned by fake Nova API.
- self.data = []
- self.data_long = []
- self.data_no_name_lookup = []
+
+class TestServerList(_TestServerList):
+
+ def setUp(self):
+ super(TestServerList, self).setUp()
Image = collections.namedtuple('Image', 'id name')
self.images_mock.return_value = [
@@ -4132,8 +4395,8 @@ class TestServerList(TestServer):
for s in self.servers
]
- for s in self.servers:
- self.data.append((
+ self.data = tuple(
+ (
s.id,
s.name,
s.status,
@@ -4141,34 +4404,8 @@ class TestServerList(TestServer):
# Image will be an empty string if boot-from-volume
self.image.name if s.image else server.IMAGE_STRING_FOR_BFV,
self.flavor.name,
- ))
- self.data_long.append((
- s.id,
- s.name,
- s.status,
- getattr(s, 'OS-EXT-STS:task_state'),
- server.PowerStateColumn(
- getattr(s, 'OS-EXT-STS:power_state')
- ),
- format_columns.DictListColumn(s.networks),
- # Image will be an empty string if boot-from-volume
- self.image.name if s.image else server.IMAGE_STRING_FOR_BFV,
- s.image['id'] if s.image else server.IMAGE_STRING_FOR_BFV,
- self.flavor.name,
- s.flavor['id'],
- getattr(s, 'OS-EXT-AZ:availability_zone'),
- getattr(s, 'OS-EXT-SRV-ATTR:host'),
- format_columns.DictColumn({}),
- ))
- self.data_no_name_lookup.append((
- s.id,
- s.name,
- s.status,
- format_columns.DictListColumn(s.networks),
- # Image will be an empty string if boot-from-volume
- s.image['id'] if s.image else server.IMAGE_STRING_FOR_BFV,
- s.flavor['id']
- ))
+ ) for s in self.servers
+ )
def test_server_list_no_option(self):
arglist = []
@@ -4189,7 +4426,7 @@ class TestServerList(TestServer):
self.assertFalse(self.flavors_mock.get.call_count)
self.assertFalse(self.get_image_mock.call_count)
self.assertEqual(self.columns, columns)
- self.assertEqual(tuple(self.data), tuple(data))
+ self.assertEqual(self.data, tuple(data))
def test_server_list_no_servers(self):
arglist = []
@@ -4208,9 +4445,28 @@ class TestServerList(TestServer):
self.assertEqual(0, self.images_mock.list.call_count)
self.assertEqual(0, self.flavors_mock.list.call_count)
self.assertEqual(self.columns, columns)
- self.assertEqual(tuple(self.data), tuple(data))
+ self.assertEqual(self.data, tuple(data))
def test_server_list_long_option(self):
+ self.data = tuple(
+ (
+ s.id,
+ s.name,
+ s.status,
+ getattr(s, 'OS-EXT-STS:task_state'),
+ server.PowerStateColumn(
+ getattr(s, 'OS-EXT-STS:power_state')
+ ),
+ format_columns.DictListColumn(s.networks),
+ # Image will be an empty string if boot-from-volume
+ self.image.name if s.image else server.IMAGE_STRING_FOR_BFV,
+ s.image['id'] if s.image else server.IMAGE_STRING_FOR_BFV,
+ self.flavor.name,
+ s.flavor['id'],
+ getattr(s, 'OS-EXT-AZ:availability_zone'),
+ getattr(s, 'OS-EXT-SRV-ATTR:host'),
+ s.Metadata,
+ ) for s in self.servers)
arglist = [
'--long',
]
@@ -4221,10 +4477,9 @@ class TestServerList(TestServer):
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
columns, data = self.cmd.take_action(parsed_args)
-
self.servers_mock.list.assert_called_with(**self.kwargs)
self.assertEqual(self.columns_long, columns)
- self.assertCountEqual(tuple(self.data_long), tuple(data))
+ self.assertEqual(self.data, tuple(data))
def test_server_list_column_option(self):
arglist = [
@@ -4246,6 +4501,18 @@ class TestServerList(TestServer):
self.assertIn('Created At', columns)
def test_server_list_no_name_lookup_option(self):
+ self.data = tuple(
+ (
+ s.id,
+ s.name,
+ s.status,
+ format_columns.DictListColumn(s.networks),
+ # Image will be an empty string if boot-from-volume
+ s.image['id'] if s.image else server.IMAGE_STRING_FOR_BFV,
+ s.flavor['id']
+ ) for s in self.servers
+ )
+
arglist = [
'--no-name-lookup',
]
@@ -4259,9 +4526,21 @@ class TestServerList(TestServer):
self.servers_mock.list.assert_called_with(**self.kwargs)
self.assertEqual(self.columns, columns)
- self.assertEqual(tuple(self.data_no_name_lookup), tuple(data))
+ self.assertEqual(self.data, tuple(data))
def test_server_list_n_option(self):
+ self.data = tuple(
+ (
+ s.id,
+ s.name,
+ s.status,
+ format_columns.DictListColumn(s.networks),
+ # Image will be an empty string if boot-from-volume
+ s.image['id'] if s.image else server.IMAGE_STRING_FOR_BFV,
+ s.flavor['id']
+ ) for s in self.servers
+ )
+
arglist = [
'-n',
]
@@ -4275,7 +4554,7 @@ class TestServerList(TestServer):
self.servers_mock.list.assert_called_with(**self.kwargs)
self.assertEqual(self.columns, columns)
- self.assertEqual(tuple(self.data_no_name_lookup), tuple(data))
+ self.assertEqual(self.data, tuple(data))
def test_server_list_name_lookup_one_by_one(self):
arglist = [
@@ -4297,7 +4576,7 @@ class TestServerList(TestServer):
self.flavors_mock.get.assert_called()
self.assertEqual(self.columns, columns)
- self.assertEqual(tuple(self.data), tuple(data))
+ self.assertEqual(self.data, tuple(data))
def test_server_list_with_image(self):
@@ -4318,81 +4597,7 @@ class TestServerList(TestServer):
self.servers_mock.list.assert_called_with(**self.kwargs)
self.assertEqual(self.columns, columns)
- self.assertEqual(tuple(self.data), tuple(data))
-
- def test_server_list_with_locked_pre_v273(self):
-
- arglist = [
- '--locked'
- ]
- verifylist = [
- ('locked', True)
- ]
-
- parsed_args = self.check_parser(self.cmd, arglist, verifylist)
- ex = self.assertRaises(exceptions.CommandError,
- self.cmd.take_action,
- parsed_args)
- self.assertIn(
- '--os-compute-api-version 2.73 or greater is required', str(ex))
-
- def test_server_list_with_locked_v273(self):
-
- self.app.client_manager.compute.api_version = \
- api_versions.APIVersion('2.73')
- arglist = [
- '--locked'
- ]
- verifylist = [
- ('locked', True)
- ]
-
- parsed_args = self.check_parser(self.cmd, arglist, verifylist)
- columns, data = self.cmd.take_action(parsed_args)
-
- self.search_opts['locked'] = True
- self.servers_mock.list.assert_called_with(**self.kwargs)
-
- self.assertEqual(self.columns, columns)
- self.assertEqual(tuple(self.data), tuple(data))
-
- def test_server_list_with_unlocked_v273(self):
-
- self.app.client_manager.compute.api_version = \
- api_versions.APIVersion('2.73')
- arglist = [
- '--unlocked'
- ]
- verifylist = [
- ('unlocked', True)
- ]
-
- parsed_args = self.check_parser(self.cmd, arglist, verifylist)
- columns, data = self.cmd.take_action(parsed_args)
-
- self.search_opts['locked'] = False
- self.servers_mock.list.assert_called_with(**self.kwargs)
-
- self.assertEqual(self.columns, columns)
- self.assertEqual(tuple(self.data), tuple(data))
-
- def test_server_list_with_locked_and_unlocked_v273(self):
-
- self.app.client_manager.compute.api_version = \
- api_versions.APIVersion('2.73')
- arglist = [
- '--locked',
- '--unlocked'
- ]
- verifylist = [
- ('locked', True),
- ('unlocked', True)
- ]
-
- ex = self.assertRaises(
- utils.ParserException,
- self.check_parser, self.cmd, arglist, verifylist)
- self.assertIn('Argument parse failed', str(ex))
+ self.assertEqual(self.data, tuple(data))
def test_server_list_with_flavor(self):
@@ -4412,7 +4617,7 @@ class TestServerList(TestServer):
self.servers_mock.list.assert_called_with(**self.kwargs)
self.assertEqual(self.columns, columns)
- self.assertEqual(tuple(self.data), tuple(data))
+ self.assertEqual(self.data, tuple(data))
def test_server_list_with_changes_since(self):
@@ -4433,7 +4638,7 @@ class TestServerList(TestServer):
self.servers_mock.list.assert_called_with(**self.kwargs)
self.assertEqual(self.columns, columns)
- self.assertEqual(tuple(self.data), tuple(data))
+ self.assertEqual(self.data, tuple(data))
@mock.patch.object(iso8601, 'parse_date', side_effect=iso8601.ParseError)
def test_server_list_with_invalid_changes_since(self, mock_parse_isotime):
@@ -4456,123 +4661,6 @@ class TestServerList(TestServer):
'Invalid time value'
)
- def test_server_list_v266_with_changes_before(self):
- self.app.client_manager.compute.api_version = (
- api_versions.APIVersion('2.66'))
- arglist = [
- '--changes-before', '2016-03-05T06:27:59Z',
- '--deleted'
- ]
- verifylist = [
- ('changes_before', '2016-03-05T06:27:59Z'),
- ('deleted', True),
- ]
-
- parsed_args = self.check_parser(self.cmd, arglist, verifylist)
- columns, data = self.cmd.take_action(parsed_args)
-
- self.search_opts['changes-before'] = '2016-03-05T06:27:59Z'
- self.search_opts['deleted'] = True
-
- self.servers_mock.list.assert_called_with(**self.kwargs)
-
- self.assertEqual(self.columns, columns)
- self.assertEqual(tuple(self.data), tuple(data))
-
- @mock.patch.object(iso8601, 'parse_date', side_effect=iso8601.ParseError)
- def test_server_list_v266_with_invalid_changes_before(
- self, mock_parse_isotime):
- self.app.client_manager.compute.api_version = (
- api_versions.APIVersion('2.66'))
-
- arglist = [
- '--changes-before', 'Invalid time value',
- ]
- verifylist = [
- ('changes_before', 'Invalid time value'),
- ]
-
- parsed_args = self.check_parser(self.cmd, arglist, verifylist)
- try:
- self.cmd.take_action(parsed_args)
- self.fail('CommandError should be raised.')
- except exceptions.CommandError as e:
- self.assertEqual('Invalid changes-before value: Invalid time '
- 'value', str(e))
- mock_parse_isotime.assert_called_once_with(
- 'Invalid time value'
- )
-
- def test_server_with_changes_before_pre_v266(self):
- self.app.client_manager.compute.api_version = (
- api_versions.APIVersion('2.65'))
-
- arglist = [
- '--changes-before', '2016-03-05T06:27:59Z',
- '--deleted'
- ]
- verifylist = [
- ('changes_before', '2016-03-05T06:27:59Z'),
- ('deleted', True),
- ]
-
- parsed_args = self.check_parser(self.cmd, arglist, verifylist)
-
- self.assertRaises(exceptions.CommandError,
- self.cmd.take_action,
- parsed_args)
-
- def test_server_list_v269_with_partial_constructs(self):
- self.app.client_manager.compute.api_version = \
- api_versions.APIVersion('2.69')
-
- arglist = []
- verifylist = []
- parsed_args = self.check_parser(self.cmd, arglist, verifylist)
-
- # include "partial results" from non-responsive part of
- # infrastructure.
- server_dict = {
- "id": "server-id-95a56bfc4xxxxxx28d7e418bfd97813a",
- "status": "UNKNOWN",
- "tenant_id": "6f70656e737461636b20342065766572",
- "created": "2018-12-03T21:06:18Z",
- "links": [
- {
- "href": "http://fake/v2.1/",
- "rel": "self"
- },
- {
- "href": "http://fake",
- "rel": "bookmark"
- }
- ],
- # We need to pass networks as {} because its defined as a property
- # of the novaclient Server class which gives {} by default. If not
- # it will fail at formatting the networks info later on.
- "networks": {}
- }
- _server = compute_fakes.fakes.FakeResource(
- info=server_dict,
- )
- self.servers.append(_server)
- columns, data = self.cmd.take_action(parsed_args)
- # get the first three servers out since our interest is in the partial
- # server.
- next(data)
- next(data)
- next(data)
- partial_server = next(data)
- expected_row = (
- 'server-id-95a56bfc4xxxxxx28d7e418bfd97813a',
- '',
- 'UNKNOWN',
- format_columns.DictListColumn({}),
- '',
- '',
- )
- self.assertEqual(expected_row, partial_server)
-
def test_server_list_with_tag(self):
self.app.client_manager.compute.api_version = api_versions.APIVersion(
'2.26')
@@ -4593,7 +4681,7 @@ class TestServerList(TestServer):
self.servers_mock.list.assert_called_with(**self.kwargs)
self.assertEqual(self.columns, columns)
- self.assertEqual(tuple(self.data), tuple(data))
+ self.assertEqual(self.data, tuple(data))
def test_server_list_with_tag_pre_v225(self):
self.app.client_manager.compute.api_version = api_versions.APIVersion(
@@ -4636,7 +4724,7 @@ class TestServerList(TestServer):
self.servers_mock.list.assert_called_with(**self.kwargs)
self.assertEqual(self.columns, columns)
- self.assertEqual(tuple(self.data), tuple(data))
+ self.assertEqual(self.data, tuple(data))
def test_server_list_with_not_tag_pre_v226(self):
self.app.client_manager.compute.api_version = api_versions.APIVersion(
@@ -4797,6 +4885,258 @@ class TestServerList(TestServer):
self.assertEqual(tuple(self.data), tuple(data))
+class TestServerListV273(_TestServerList):
+
+ # Columns to be listed up.
+ columns = (
+ 'ID',
+ 'Name',
+ 'Status',
+ 'Networks',
+ 'Image',
+ 'Flavor',
+ )
+ columns_long = (
+ 'ID',
+ 'Name',
+ 'Status',
+ 'Task State',
+ 'Power State',
+ 'Networks',
+ 'Image Name',
+ 'Image ID',
+ 'Flavor',
+ 'Availability Zone',
+ 'Host',
+ 'Properties',
+ )
+
+ def setUp(self):
+ super(TestServerListV273, self).setUp()
+
+ # The fake servers' attributes. Use the original attributes names in
+ # nova, not the ones printed by "server list" command.
+ self.attrs['flavor'] = {
+ 'vcpus': self.flavor.vcpus,
+ 'ram': self.flavor.ram,
+ 'disk': self.flavor.disk,
+ 'ephemeral': self.flavor.ephemeral,
+ 'swap': self.flavor.swap,
+ 'original_name': self.flavor.name,
+ 'extra_specs': self.flavor.extra_specs,
+ }
+
+ # The servers to be listed.
+ self.servers = self.setup_servers_mock(3)
+ self.servers_mock.list.return_value = self.servers
+
+ Image = collections.namedtuple('Image', 'id name')
+ self.images_mock.return_value = [
+ Image(id=s.image['id'], name=self.image.name)
+ # Image will be an empty string if boot-from-volume
+ for s in self.servers if s.image
+ ]
+
+ # The flavor information is embedded, so now reason for this to be
+ # called
+ self.flavors_mock.list = mock.NonCallableMock()
+
+ self.data = tuple(
+ (
+ s.id,
+ s.name,
+ s.status,
+ format_columns.DictListColumn(s.networks),
+ # Image will be an empty string if boot-from-volume
+ self.image.name if s.image else server.IMAGE_STRING_FOR_BFV,
+ self.flavor.name,
+ ) for s in self.servers)
+
+ def test_server_list_with_locked_pre_v273(self):
+
+ arglist = [
+ '--locked'
+ ]
+ verifylist = [
+ ('locked', True)
+ ]
+
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+ ex = self.assertRaises(exceptions.CommandError,
+ self.cmd.take_action,
+ parsed_args)
+ self.assertIn(
+ '--os-compute-api-version 2.73 or greater is required', str(ex))
+
+ def test_server_list_with_locked(self):
+
+ self.app.client_manager.compute.api_version = \
+ api_versions.APIVersion('2.73')
+ arglist = [
+ '--locked'
+ ]
+ verifylist = [
+ ('locked', True)
+ ]
+
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+ columns, data = self.cmd.take_action(parsed_args)
+
+ self.search_opts['locked'] = True
+ self.servers_mock.list.assert_called_with(**self.kwargs)
+
+ self.assertItemsEqual(self.columns, columns)
+ self.assertItemsEqual(self.data, tuple(data))
+
+ def test_server_list_with_unlocked_v273(self):
+
+ self.app.client_manager.compute.api_version = \
+ api_versions.APIVersion('2.73')
+ arglist = [
+ '--unlocked'
+ ]
+ verifylist = [
+ ('unlocked', True)
+ ]
+
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+ columns, data = self.cmd.take_action(parsed_args)
+
+ self.search_opts['locked'] = False
+ self.servers_mock.list.assert_called_with(**self.kwargs)
+
+ self.assertItemsEqual(self.columns, columns)
+ self.assertItemsEqual(self.data, tuple(data))
+
+ def test_server_list_with_locked_and_unlocked(self):
+
+ self.app.client_manager.compute.api_version = \
+ api_versions.APIVersion('2.73')
+ arglist = [
+ '--locked',
+ '--unlocked'
+ ]
+ verifylist = [
+ ('locked', True),
+ ('unlocked', True)
+ ]
+
+ ex = self.assertRaises(
+ utils.ParserException,
+ self.check_parser, self.cmd, arglist, verifylist)
+ self.assertIn('Argument parse failed', str(ex))
+
+ def test_server_list_with_changes_before(self):
+ self.app.client_manager.compute.api_version = (
+ api_versions.APIVersion('2.66'))
+ arglist = [
+ '--changes-before', '2016-03-05T06:27:59Z',
+ '--deleted'
+ ]
+ verifylist = [
+ ('changes_before', '2016-03-05T06:27:59Z'),
+ ('deleted', True),
+ ]
+
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+ columns, data = self.cmd.take_action(parsed_args)
+
+ self.search_opts['changes-before'] = '2016-03-05T06:27:59Z'
+ self.search_opts['deleted'] = True
+
+ self.servers_mock.list.assert_called_with(**self.kwargs)
+
+ self.assertItemsEqual(self.columns, columns)
+ self.assertItemsEqual(self.data, tuple(data))
+
+ @mock.patch.object(iso8601, 'parse_date', side_effect=iso8601.ParseError)
+ def test_server_list_with_invalid_changes_before(
+ self, mock_parse_isotime):
+ self.app.client_manager.compute.api_version = (
+ api_versions.APIVersion('2.66'))
+
+ arglist = [
+ '--changes-before', 'Invalid time value',
+ ]
+ verifylist = [
+ ('changes_before', 'Invalid time value'),
+ ]
+
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+ try:
+ self.cmd.take_action(parsed_args)
+ self.fail('CommandError should be raised.')
+ except exceptions.CommandError as e:
+ self.assertEqual('Invalid changes-before value: Invalid time '
+ 'value', str(e))
+ mock_parse_isotime.assert_called_once_with(
+ 'Invalid time value'
+ )
+
+ def test_server_with_changes_before_pre_v266(self):
+ self.app.client_manager.compute.api_version = (
+ api_versions.APIVersion('2.65'))
+
+ arglist = [
+ '--changes-before', '2016-03-05T06:27:59Z',
+ '--deleted'
+ ]
+ verifylist = [
+ ('changes_before', '2016-03-05T06:27:59Z'),
+ ('deleted', True),
+ ]
+
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ self.assertRaises(exceptions.CommandError,
+ self.cmd.take_action,
+ parsed_args)
+
+ def test_server_list_v269_with_partial_constructs(self):
+ self.app.client_manager.compute.api_version = \
+ api_versions.APIVersion('2.69')
+ arglist = []
+ verifylist = []
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+ # include "partial results" from non-responsive part of
+ # infrastructure.
+ server_dict = {
+ "id": "server-id-95a56bfc4xxxxxx28d7e418bfd97813a",
+ "status": "UNKNOWN",
+ "tenant_id": "6f70656e737461636b20342065766572",
+ "created": "2018-12-03T21:06:18Z",
+ "links": [
+ {
+ "href": "http://fake/v2.1/",
+ "rel": "self"
+ },
+ {
+ "href": "http://fake",
+ "rel": "bookmark"
+ }
+ ],
+ # We need to pass networks as {} because its defined as a property
+ # of the novaclient Server class which gives {} by default. If not
+ # it will fail at formatting the networks info later on.
+ "networks": {}
+ }
+ server = compute_fakes.fakes.FakeResource(
+ info=server_dict,
+ )
+ self.servers.append(server)
+ columns, data = self.cmd.take_action(parsed_args)
+ # get the first three servers out since our interest is in the partial
+ # server.
+ next(data)
+ next(data)
+ next(data)
+ partial_server = next(data)
+ expected_row = (
+ 'server-id-95a56bfc4xxxxxx28d7e418bfd97813a', '',
+ 'UNKNOWN', format_columns.DictListColumn({}), '', '')
+ self.assertEqual(expected_row, partial_server)
+
+
class TestServerLock(TestServer):
def setUp(self):
@@ -4911,7 +5251,7 @@ class TestServerMigrate(TestServer):
verifylist = [
('live_migration', False),
('block_migration', None),
- ('disk_overcommit', False),
+ ('disk_overcommit', None),
('wait', False),
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
@@ -4933,7 +5273,7 @@ class TestServerMigrate(TestServer):
('live_migration', False),
('host', 'fakehost'),
('block_migration', None),
- ('disk_overcommit', False),
+ ('disk_overcommit', None),
('wait', False),
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
@@ -4955,7 +5295,7 @@ class TestServerMigrate(TestServer):
verifylist = [
('live_migration', False),
('block_migration', True),
- ('disk_overcommit', False),
+ ('disk_overcommit', None),
('wait', False),
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
@@ -4986,7 +5326,7 @@ class TestServerMigrate(TestServer):
self.assertNotCalled(self.servers_mock.live_migrate)
self.assertNotCalled(self.servers_mock.migrate)
- def test_server_migrate_with_host_pre_2_56(self):
+ def test_server_migrate_with_host_pre_v256(self):
# Tests that --host is not allowed for a cold migration
# before microversion 2.56 (the test defaults to 2.1).
arglist = [
@@ -4996,7 +5336,7 @@ class TestServerMigrate(TestServer):
('live_migration', False),
('host', 'fakehost'),
('block_migration', None),
- ('disk_overcommit', False),
+ ('disk_overcommit', None),
('wait', False),
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
@@ -5022,7 +5362,7 @@ class TestServerMigrate(TestServer):
('live_migration', True),
('host', None),
('block_migration', None),
- ('disk_overcommit', False),
+ ('disk_overcommit', None),
('wait', False),
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
@@ -5046,7 +5386,7 @@ class TestServerMigrate(TestServer):
('live_migration', True),
('host', 'fakehost'),
('block_migration', None),
- ('disk_overcommit', False),
+ ('disk_overcommit', None),
('wait', False),
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
@@ -5074,7 +5414,7 @@ class TestServerMigrate(TestServer):
('live_migration', True),
('host', 'fakehost'),
('block_migration', None),
- ('disk_overcommit', False),
+ ('disk_overcommit', None),
('wait', False),
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
@@ -5101,7 +5441,7 @@ class TestServerMigrate(TestServer):
verifylist = [
('live_migration', True),
('block_migration', True),
- ('disk_overcommit', False),
+ ('disk_overcommit', None),
('wait', False),
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
@@ -5187,7 +5527,7 @@ class TestServerMigrate(TestServer):
verifylist = [
('live_migration', False),
('block_migration', None),
- ('disk_overcommit', False),
+ ('disk_overcommit', None),
('wait', True),
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
@@ -5207,7 +5547,7 @@ class TestServerMigrate(TestServer):
verifylist = [
('live_migration', False),
('block_migration', None),
- ('disk_overcommit', False),
+ ('disk_overcommit', None),
('wait', True),
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
@@ -6009,10 +6349,10 @@ class TestServerPause(TestServer):
}
def test_server_pause_one_server(self):
- self.run_method_with_servers('pause', 1)
+ self.run_method_with_sdk_servers('pause_server', 1)
def test_server_pause_multi_servers(self):
- self.run_method_with_servers('pause', 3)
+ self.run_method_with_sdk_servers('pause_server', 3)
class TestServerRebuild(TestServer):
@@ -6403,7 +6743,7 @@ class TestServerRebuild(TestServer):
self.image, None,
userdata=mock_file,)
- def test_rebuild_with_user_data_pre_257(self):
+ def test_rebuild_with_user_data_pre_v257(self):
self.app.client_manager.compute.api_version = \
api_versions.APIVersion('2.56')
@@ -6443,7 +6783,7 @@ class TestServerRebuild(TestServer):
self.server.rebuild.assert_called_with(
self.image, None, userdata=None)
- def test_rebuild_with_no_user_data_pre_254(self):
+ def test_rebuild_with_no_user_data_pre_v254(self):
self.app.client_manager.compute.api_version = \
api_versions.APIVersion('2.53')
@@ -6535,7 +6875,7 @@ class TestServerRebuild(TestServer):
self.server.rebuild.assert_called_with(
self.image, None, trusted_image_certificates=None)
- def test_rebuild_with_no_trusted_image_cert_pre_257(self):
+ def test_rebuild_with_no_trusted_image_cert_pre_v263(self):
self.app.client_manager.compute.api_version = \
api_versions.APIVersion('2.62')
@@ -6959,14 +7299,14 @@ class TestServerRemovePort(TestServer):
# Set method to be tested.
self.methods = {
- 'interface_detach': None,
+ 'delete_server_interface': None,
}
self.find_port = mock.Mock()
self.app.client_manager.network.find_port = self.find_port
def _test_server_remove_port(self, port_id):
- servers = self.setup_servers_mock(count=1)
+ servers = self.setup_sdk_servers_mock(count=1)
port = 'fake-port'
arglist = [
@@ -6981,7 +7321,8 @@ class TestServerRemovePort(TestServer):
result = self.cmd.take_action(parsed_args)
- servers[0].interface_detach.assert_called_once_with(port_id)
+ self.sdk_client.delete_server_interface.assert_called_with(
+ port_id, server=servers[0], ignore_missing=False)
self.assertIsNone(result)
def test_server_remove_port(self):
@@ -7006,17 +7347,18 @@ class TestServerRemoveNetwork(TestServer):
# Set method to be tested.
self.fake_inf = mock.Mock()
self.methods = {
- 'interface_list': [self.fake_inf],
- 'interface_detach': None,
+ 'server_interfaces': [self.fake_inf],
+ 'delete_server_interface': None,
}
self.find_network = mock.Mock()
self.app.client_manager.network.find_network = self.find_network
+ self.sdk_client.server_interfaces.return_value = [self.fake_inf]
def _test_server_remove_network(self, network_id):
self.fake_inf.net_id = network_id
self.fake_inf.port_id = 'fake-port'
- servers = self.setup_servers_mock(count=1)
+ servers = self.setup_sdk_servers_mock(count=1)
network = 'fake-network'
arglist = [
@@ -7031,8 +7373,9 @@ class TestServerRemoveNetwork(TestServer):
result = self.cmd.take_action(parsed_args)
- servers[0].interface_list.assert_called_once_with()
- servers[0].interface_detach.assert_called_once_with('fake-port')
+ self.sdk_client.server_interfaces.assert_called_once_with(servers[0])
+ self.sdk_client.delete_server_interface.assert_called_once_with(
+ 'fake-port', server=servers[0])
self.assertIsNone(result)
def test_server_remove_network(self):
@@ -7548,10 +7891,10 @@ class TestServerResume(TestServer):
}
def test_server_resume_one_server(self):
- self.run_method_with_servers('resume', 1)
+ self.run_method_with_sdk_servers('resume_server', 1)
def test_server_resume_multi_servers(self):
- self.run_method_with_servers('resume', 3)
+ self.run_method_with_sdk_servers('resume_server', 3)
class TestServerSet(TestServer):
@@ -8117,6 +8460,123 @@ class TestServerShow(TestServer):
exceptions.CommandError, self.cmd.take_action, parsed_args)
+@mock.patch('openstackclient.compute.v2.server.os.system')
+class TestServerSsh(TestServer):
+
+ def setUp(self):
+ super().setUp()
+
+ self.cmd = server.SshServer(self.app, None)
+
+ self.app.client_manager.auth_ref = mock.Mock()
+ self.app.client_manager.auth_ref.username = 'cloud'
+
+ self.attrs = {
+ 'addresses': {
+ 'public': [
+ {
+ 'addr': '192.168.1.30',
+ 'OS-EXT-IPS-MAC:mac_addr': '00:0c:29:0d:11:74',
+ 'OS-EXT-IPS:type': 'fixed',
+ 'version': 4,
+ },
+ ],
+ },
+ }
+ self.server = compute_fakes.FakeServer.create_one_server(
+ attrs=self.attrs, methods=self.methods,
+ )
+ self.servers_mock.get.return_value = self.server
+
+ def test_server_ssh_no_opts(self, mock_exec):
+ arglist = [
+ self.server.name,
+ ]
+ verifylist = [
+ ('server', self.server.name),
+ ('login', None),
+ ('port', None),
+ ('identity', None),
+ ('option', None),
+ ('ipv4', False),
+ ('ipv6', False),
+ ('address_type', 'public'),
+ ('verbose', False),
+ ('ssh_args', []),
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ with mock.patch.object(self.cmd.log, 'warning') as mock_warning:
+ result = self.cmd.take_action(parsed_args)
+
+ self.assertIsNone(result)
+ mock_exec.assert_called_once_with('ssh 192.168.1.30 -l cloud')
+ mock_warning.assert_not_called()
+
+ def test_server_ssh_passthrough_opts(self, mock_exec):
+ arglist = [
+ self.server.name,
+ '--',
+ '-l', 'username',
+ '-p', '2222',
+ ]
+ verifylist = [
+ ('server', self.server.name),
+ ('login', None),
+ ('port', None),
+ ('identity', None),
+ ('option', None),
+ ('ipv4', False),
+ ('ipv6', False),
+ ('address_type', 'public'),
+ ('verbose', False),
+ ('ssh_args', ['-l', 'username', '-p', '2222']),
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ with mock.patch.object(self.cmd.log, 'warning') as mock_warning:
+ result = self.cmd.take_action(parsed_args)
+
+ self.assertIsNone(result)
+ mock_exec.assert_called_once_with(
+ 'ssh 192.168.1.30 -l username -p 2222'
+ )
+ mock_warning.assert_not_called()
+
+ def test_server_ssh_deprecated_opts(self, mock_exec):
+ arglist = [
+ self.server.name,
+ '-l', 'username',
+ '-p', '2222',
+ ]
+ verifylist = [
+ ('server', self.server.name),
+ ('login', 'username'),
+ ('port', 2222),
+ ('identity', None),
+ ('option', None),
+ ('ipv4', False),
+ ('ipv6', False),
+ ('address_type', 'public'),
+ ('verbose', False),
+ ('ssh_args', []),
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ with mock.patch.object(self.cmd.log, 'warning') as mock_warning:
+ result = self.cmd.take_action(parsed_args)
+
+ self.assertIsNone(result)
+ mock_exec.assert_called_once_with(
+ 'ssh 192.168.1.30 -p 2222 -l username'
+ )
+ mock_warning.assert_called_once()
+ self.assertIn(
+ 'The ssh options have been deprecated.',
+ mock_warning.call_args[0][0],
+ )
+
+
class TestServerStart(TestServer):
def setUp(self):
@@ -8215,10 +8675,10 @@ class TestServerSuspend(TestServer):
}
def test_server_suspend_one_server(self):
- self.run_method_with_servers('suspend', 1)
+ self.run_method_with_sdk_servers('suspend_server', 1)
def test_server_suspend_multi_servers(self):
- self.run_method_with_servers('suspend', 3)
+ self.run_method_with_sdk_servers('suspend_server', 3)
class TestServerUnlock(TestServer):
@@ -8255,10 +8715,10 @@ class TestServerUnpause(TestServer):
}
def test_server_unpause_one_server(self):
- self.run_method_with_servers('unpause', 1)
+ self.run_method_with_sdk_servers('unpause_server', 1)
def test_server_unpause_multi_servers(self):
- self.run_method_with_servers('unpause', 3)
+ self.run_method_with_sdk_servers('unpause_server', 3)
class TestServerUnset(TestServer):
diff --git a/openstackclient/tests/unit/compute/v2/test_server_image.py b/openstackclient/tests/unit/compute/v2/test_server_image.py
index e1740169..db0e1d9e 100644
--- a/openstackclient/tests/unit/compute/v2/test_server_image.py
+++ b/openstackclient/tests/unit/compute/v2/test_server_image.py
@@ -27,8 +27,9 @@ class TestServerImage(compute_fakes.TestComputev2):
super(TestServerImage, self).setUp()
# Get a shortcut to the compute client ServerManager Mock
- self.servers_mock = self.app.client_manager.compute.servers
- self.servers_mock.reset_mock()
+ self.app.client_manager.sdk_connection = mock.Mock()
+ self.app.client_manager.sdk_connection.compute = mock.Mock()
+ self.sdk_client = self.app.client_manager.sdk_connection.compute
# Get a shortcut to the image client ImageManager Mock
self.images_mock = self.app.client_manager.image
@@ -41,14 +42,14 @@ class TestServerImage(compute_fakes.TestComputev2):
self.methods = {}
def setup_servers_mock(self, count):
- servers = compute_fakes.FakeServer.create_servers(
+ servers = compute_fakes.FakeServer.create_sdk_servers(
attrs=self.attrs,
methods=self.methods,
count=count,
)
- # This is the return value for utils.find_resource()
- self.servers_mock.get = compute_fakes.FakeServer.get_servers(
+ # This is the return value for compute_client.find_server()
+ self.sdk_client.find_server = compute_fakes.FakeServer.get_servers(
servers,
0,
)
@@ -104,8 +105,8 @@ class TestServerImageCreate(TestServerImage):
)
self.images_mock.find_image = mock.Mock(side_effect=images)
- self.servers_mock.create_image = mock.Mock(
- return_value=images[0].id,
+ self.sdk_client.create_server_image = mock.Mock(
+ return_value=images[0],
)
return images
@@ -126,8 +127,7 @@ class TestServerImageCreate(TestServerImage):
# data to be shown.
columns, data = self.cmd.take_action(parsed_args)
- # ServerManager.create_image(server, image_name, metadata=)
- self.servers_mock.create_image.assert_called_with(
+ self.sdk_client.create_server_image.assert_called_with(
servers[0].id,
servers[0].name,
None,
@@ -157,8 +157,7 @@ class TestServerImageCreate(TestServerImage):
# data to be shown.
columns, data = self.cmd.take_action(parsed_args)
- # ServerManager.create_image(server, image_name, metadata=)
- self.servers_mock.create_image.assert_called_with(
+ self.sdk_client.create_server_image.assert_called_with(
servers[0].id,
'img-nam',
{'key': 'value'},
@@ -188,8 +187,7 @@ class TestServerImageCreate(TestServerImage):
parsed_args,
)
- # ServerManager.create_image(server, image_name, metadata=)
- self.servers_mock.create_image.assert_called_with(
+ self.sdk_client.create_server_image.assert_called_with(
servers[0].id,
servers[0].name,
None,
@@ -221,8 +219,7 @@ class TestServerImageCreate(TestServerImage):
# data to be shown.
columns, data = self.cmd.take_action(parsed_args)
- # ServerManager.create_image(server, image_name, metadata=)
- self.servers_mock.create_image.assert_called_with(
+ self.sdk_client.create_server_image.assert_called_with(
servers[0].id,
servers[0].name,
None,
diff --git a/openstackclient/tests/unit/compute/v2/test_service.py b/openstackclient/tests/unit/compute/v2/test_service.py
index c547c3a6..5b58431a 100644
--- a/openstackclient/tests/unit/compute/v2/test_service.py
+++ b/openstackclient/tests/unit/compute/v2/test_service.py
@@ -17,6 +17,7 @@ from unittest import mock
from unittest.mock import call
from novaclient import api_versions
+from openstack import utils as sdk_utils
from osc_lib import exceptions
from openstackclient.compute.v2 import service
@@ -28,9 +29,9 @@ class TestService(compute_fakes.TestComputev2):
def setUp(self):
super(TestService, self).setUp()
- # Get a shortcut to the ServiceManager Mock
- self.service_mock = self.app.client_manager.compute.services
- self.service_mock.reset_mock()
+ self.app.client_manager.sdk_connection = mock.Mock()
+ self.app.client_manager.sdk_connection.compute = mock.Mock()
+ self.sdk_client = self.app.client_manager.sdk_connection.compute
class TestServiceDelete(TestService):
@@ -40,7 +41,7 @@ class TestServiceDelete(TestService):
def setUp(self):
super(TestServiceDelete, self).setUp()
- self.service_mock.delete.return_value = None
+ self.sdk_client.delete_service.return_value = None
# Get the command object to test
self.cmd = service.DeleteService(self.app, None)
@@ -56,8 +57,9 @@ class TestServiceDelete(TestService):
result = self.cmd.take_action(parsed_args)
- self.service_mock.delete.assert_called_with(
+ self.sdk_client.delete_service.assert_called_with(
self.services[0].binary,
+ ignore_missing=False
)
self.assertIsNone(result)
@@ -74,8 +76,8 @@ class TestServiceDelete(TestService):
calls = []
for s in self.services:
- calls.append(call(s.binary))
- self.service_mock.delete.assert_has_calls(calls)
+ calls.append(call(s.binary, ignore_missing=False))
+ self.sdk_client.delete_service.assert_has_calls(calls)
self.assertIsNone(result)
def test_multi_services_delete_with_exception(self):
@@ -89,7 +91,7 @@ class TestServiceDelete(TestService):
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
delete_mock_result = [None, exceptions.CommandError]
- self.service_mock.delete = (
+ self.sdk_client.delete_service = (
mock.Mock(side_effect=delete_mock_result)
)
@@ -100,8 +102,14 @@ class TestServiceDelete(TestService):
self.assertEqual(
'1 of 2 compute services failed to delete.', str(e))
- self.service_mock.delete.assert_any_call(self.services[0].binary)
- self.service_mock.delete.assert_any_call('unexist_service')
+ self.sdk_client.delete_service.assert_any_call(
+ self.services[0].binary,
+ ignore_missing=False
+ )
+ self.sdk_client.delete_service.assert_any_call(
+ 'unexist_service',
+ ignore_missing=False
+ )
class TestServiceList(TestService):
@@ -125,7 +133,7 @@ class TestServiceList(TestService):
service.id,
service.binary,
service.host,
- service.zone,
+ service.availability_zone,
service.status,
service.state,
service.updated_at,
@@ -135,7 +143,7 @@ class TestServiceList(TestService):
def setUp(self):
super(TestServiceList, self).setUp()
- self.service_mock.list.return_value = [self.service]
+ self.sdk_client.services.return_value = [self.service]
# Get the command object to test
self.cmd = service.ListService(self.app, None)
@@ -156,15 +164,18 @@ class TestServiceList(TestService):
# containing the data to be listed.
columns, data = self.cmd.take_action(parsed_args)
- self.service_mock.list.assert_called_with(
- self.service.host,
- self.service.binary,
+ self.sdk_client.services.assert_called_with(
+ host=self.service.host,
+ binary=self.service.binary,
)
self.assertEqual(self.columns, columns)
self.assertEqual(self.data, list(data))
- def test_service_list_with_long_option(self):
+ @mock.patch.object(sdk_utils, 'supports_microversion')
+ def test_service_list_with_long_option(self, sm_mock):
+ sm_mock.return_value = False
+
arglist = [
'--host', self.service.host,
'--service', self.service.binary,
@@ -182,15 +193,18 @@ class TestServiceList(TestService):
# containing the data to be listed.
columns, data = self.cmd.take_action(parsed_args)
- self.service_mock.list.assert_called_with(
- self.service.host,
- self.service.binary,
+ self.sdk_client.services.assert_called_with(
+ host=self.service.host,
+ binary=self.service.binary,
)
self.assertEqual(self.columns_long, columns)
self.assertEqual(self.data_long, list(data))
- def test_service_list_with_long_option_2_11(self):
+ @mock.patch.object(sdk_utils, 'supports_microversion')
+ def test_service_list_with_long_option_2_11(self, sm_mock):
+ sm_mock.return_value = True
+
arglist = [
'--host', self.service.host,
'--service', self.service.binary,
@@ -210,14 +224,14 @@ class TestServiceList(TestService):
# containing the data to be listed.
columns, data = self.cmd.take_action(parsed_args)
- self.service_mock.list.assert_called_with(
- self.service.host,
- self.service.binary,
+ self.sdk_client.services.assert_called_with(
+ host=self.service.host,
+ binary=self.service.binary,
)
# In 2.11 there is also a forced_down column.
columns_long = self.columns_long + ('Forced Down',)
- data_long = [self.data_long[0] + (self.service.forced_down,)]
+ data_long = [self.data_long[0] + (self.service.is_forced_down,)]
self.assertEqual(columns_long, columns)
self.assertEqual(data_long, list(data))
@@ -230,12 +244,14 @@ class TestServiceSet(TestService):
self.service = compute_fakes.FakeService.create_one_service()
- self.service_mock.enable.return_value = self.service
- self.service_mock.disable.return_value = self.service
+ self.sdk_client.enable_service.return_value = self.service
+ self.sdk_client.disable_service.return_value = self.service
self.cmd = service.SetService(self.app, None)
- def test_set_nothing(self):
+ @mock.patch.object(sdk_utils, 'supports_microversion')
+ def test_set_nothing(self, sm_mock):
+ sm_mock.return_value = False
arglist = [
self.service.host,
self.service.binary,
@@ -247,12 +263,13 @@ class TestServiceSet(TestService):
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
result = self.cmd.take_action(parsed_args)
- self.service_mock.enable.assert_not_called()
- self.service_mock.disable.assert_not_called()
- self.service_mock.disable_log_reason.assert_not_called()
+ self.sdk_client.enable_service.assert_not_called()
+ self.sdk_client.disable_service.assert_not_called()
self.assertIsNone(result)
- def test_service_set_enable(self):
+ @mock.patch.object(sdk_utils, 'supports_microversion')
+ def test_service_set_enable(self, sm_mock):
+ sm_mock.return_value = False
arglist = [
'--enable',
self.service.host,
@@ -267,13 +284,16 @@ class TestServiceSet(TestService):
result = self.cmd.take_action(parsed_args)
- self.service_mock.enable.assert_called_with(
+ self.sdk_client.enable_service.assert_called_with(
+ None,
self.service.host,
self.service.binary
)
self.assertIsNone(result)
- def test_service_set_disable(self):
+ @mock.patch.object(sdk_utils, 'supports_microversion')
+ def test_service_set_disable(self, sm_mock):
+ sm_mock.return_value = False
arglist = [
'--disable',
self.service.host,
@@ -288,13 +308,17 @@ class TestServiceSet(TestService):
result = self.cmd.take_action(parsed_args)
- self.service_mock.disable.assert_called_with(
+ self.sdk_client.disable_service.assert_called_with(
+ None,
self.service.host,
- self.service.binary
+ self.service.binary,
+ None
)
self.assertIsNone(result)
- def test_service_set_disable_with_reason(self):
+ @mock.patch.object(sdk_utils, 'supports_microversion')
+ def test_service_set_disable_with_reason(self, sm_mock):
+ sm_mock.return_value = False
reason = 'earthquake'
arglist = [
'--disable',
@@ -312,14 +336,17 @@ class TestServiceSet(TestService):
result = self.cmd.take_action(parsed_args)
- self.service_mock.disable_log_reason.assert_called_with(
+ self.sdk_client.disable_service.assert_called_with(
+ None,
self.service.host,
self.service.binary,
reason
)
self.assertIsNone(result)
- def test_service_set_only_with_disable_reason(self):
+ @mock.patch.object(sdk_utils, 'supports_microversion')
+ def test_service_set_only_with_disable_reason(self, sm_mock):
+ sm_mock.return_value = False
reason = 'earthquake'
arglist = [
'--disable-reason', reason,
@@ -339,7 +366,9 @@ class TestServiceSet(TestService):
self.assertEqual("Cannot specify option --disable-reason without "
"--disable specified.", str(e))
- def test_service_set_enable_with_disable_reason(self):
+ @mock.patch.object(sdk_utils, 'supports_microversion')
+ def test_service_set_enable_with_disable_reason(self, sm_mock):
+ sm_mock.return_value = False
reason = 'earthquake'
arglist = [
'--enable',
@@ -361,7 +390,9 @@ class TestServiceSet(TestService):
self.assertEqual("Cannot specify option --disable-reason without "
"--disable specified.", str(e))
- def test_service_set_state_up(self):
+ @mock.patch.object(sdk_utils, 'supports_microversion')
+ def test_service_set_state_up(self, sm_mock):
+ sm_mock.side_effect = [False, True]
arglist = [
'--up',
self.service.host,
@@ -373,16 +404,20 @@ class TestServiceSet(TestService):
('service', self.service.binary),
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
- self.app.client_manager.compute.api_version = api_versions.APIVersion(
- '2.11')
result = self.cmd.take_action(parsed_args)
- self.service_mock.force_down.assert_called_once_with(
- self.service.host, self.service.binary, False)
- self.assertNotCalled(self.service_mock.enable)
- self.assertNotCalled(self.service_mock.disable)
+ self.sdk_client.update_service_forced_down.assert_called_once_with(
+ None,
+ self.service.host,
+ self.service.binary,
+ False
+ )
+ self.assertNotCalled(self.sdk_client.enable_service)
+ self.assertNotCalled(self.sdk_client.disable_service)
self.assertIsNone(result)
- def test_service_set_state_down(self):
+ @mock.patch.object(sdk_utils, 'supports_microversion')
+ def test_service_set_state_down(self, sm_mock):
+ sm_mock.side_effect = [False, True]
arglist = [
'--down',
self.service.host,
@@ -394,16 +429,20 @@ class TestServiceSet(TestService):
('service', self.service.binary),
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
- self.app.client_manager.compute.api_version = api_versions.APIVersion(
- '2.11')
result = self.cmd.take_action(parsed_args)
- self.service_mock.force_down.assert_called_once_with(
- self.service.host, self.service.binary, True)
- self.assertNotCalled(self.service_mock.enable)
- self.assertNotCalled(self.service_mock.disable)
+ self.sdk_client.update_service_forced_down.assert_called_once_with(
+ None,
+ self.service.host,
+ self.service.binary,
+ True
+ )
+ self.assertNotCalled(self.sdk_client.enable_service)
+ self.assertNotCalled(self.sdk_client.disable_service)
self.assertIsNone(result)
- def test_service_set_enable_and_state_down(self):
+ @mock.patch.object(sdk_utils, 'supports_microversion')
+ def test_service_set_enable_and_state_down(self, sm_mock):
+ sm_mock.side_effect = [False, True]
arglist = [
'--enable',
'--down',
@@ -417,16 +456,23 @@ class TestServiceSet(TestService):
('service', self.service.binary),
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
- self.app.client_manager.compute.api_version = api_versions.APIVersion(
- '2.11')
result = self.cmd.take_action(parsed_args)
- self.service_mock.enable.assert_called_once_with(
- self.service.host, self.service.binary)
- self.service_mock.force_down.assert_called_once_with(
- self.service.host, self.service.binary, True)
+ self.sdk_client.enable_service.assert_called_once_with(
+ None,
+ self.service.host,
+ self.service.binary
+ )
+ self.sdk_client.update_service_forced_down.assert_called_once_with(
+ None,
+ self.service.host,
+ self.service.binary,
+ True
+ )
self.assertIsNone(result)
- def test_service_set_enable_and_state_down_with_exception(self):
+ @mock.patch.object(sdk_utils, 'supports_microversion')
+ def test_service_set_enable_and_state_down_with_exception(self, sm_mock):
+ sm_mock.side_effect = [False, True]
arglist = [
'--enable',
'--down',
@@ -441,18 +487,22 @@ class TestServiceSet(TestService):
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
- self.app.client_manager.compute.api_version = api_versions.APIVersion(
- '2.11')
- with mock.patch.object(self.service_mock, 'enable',
+ with mock.patch.object(self.sdk_client, 'enable_service',
side_effect=Exception()):
self.assertRaises(exceptions.CommandError,
self.cmd.take_action, parsed_args)
- self.service_mock.force_down.assert_called_once_with(
- self.service.host, self.service.binary, True)
-
- def test_service_set_2_53_disable_down(self):
+ self.sdk_client.update_service_forced_down.assert_called_once_with(
+ None,
+ self.service.host,
+ self.service.binary,
+ True
+ )
+
+ @mock.patch.object(sdk_utils, 'supports_microversion')
+ def test_service_set_2_53_disable_down(self, sm_mock):
# Tests disabling and forcing down a compute service with microversion
# 2.53 which requires looking up the service by host and binary.
+ sm_mock.return_value = True
arglist = [
'--disable',
'--down',
@@ -466,18 +516,27 @@ class TestServiceSet(TestService):
('service', self.service.binary),
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
- self.app.client_manager.compute.api_version = api_versions.APIVersion(
- '2.53')
service_id = '339478d0-0b95-4a94-be63-d5be05dfeb1c'
- self.service_mock.list.return_value = [mock.Mock(id=service_id)]
+ self.sdk_client.services.return_value = [mock.Mock(id=service_id)]
result = self.cmd.take_action(parsed_args)
- self.service_mock.disable.assert_called_once_with(service_id)
- self.service_mock.force_down.assert_called_once_with(service_id, True)
+ self.sdk_client.disable_service.assert_called_once_with(
+ service_id,
+ self.service.host,
+ self.service.binary,
+ None
+ )
+ self.sdk_client.update_service_forced_down.assert_called_once_with(
+ service_id,
+ self.service.host,
+ self.service.binary,
+ True)
self.assertIsNone(result)
- def test_service_set_2_53_disable_reason(self):
+ @mock.patch.object(sdk_utils, 'supports_microversion')
+ def test_service_set_2_53_disable_reason(self, sm_mock):
# Tests disabling with reason a compute service with microversion
# 2.53 which requires looking up the service by host and binary.
+ sm_mock.return_value = True
reason = 'earthquake'
arglist = [
'--disable',
@@ -492,18 +551,22 @@ class TestServiceSet(TestService):
('service', self.service.binary),
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
- self.app.client_manager.compute.api_version = api_versions.APIVersion(
- '2.53')
service_id = '339478d0-0b95-4a94-be63-d5be05dfeb1c'
- self.service_mock.list.return_value = [mock.Mock(id=service_id)]
+ self.sdk_client.services.return_value = [mock.Mock(id=service_id)]
result = self.cmd.take_action(parsed_args)
- self.service_mock.disable_log_reason.assert_called_once_with(
- service_id, reason)
+ self.sdk_client.disable_service.assert_called_once_with(
+ service_id,
+ self.service.host,
+ self.service.binary,
+ reason
+ )
self.assertIsNone(result)
- def test_service_set_2_53_enable_up(self):
+ @mock.patch.object(sdk_utils, 'supports_microversion')
+ def test_service_set_2_53_enable_up(self, sm_mock):
# Tests enabling and bringing up a compute service with microversion
# 2.53 which requires looking up the service by host and binary.
+ sm_mock.return_value = True
arglist = [
'--enable',
'--up',
@@ -517,30 +580,37 @@ class TestServiceSet(TestService):
('service', self.service.binary),
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
- self.app.client_manager.compute.api_version = api_versions.APIVersion(
- '2.53')
service_id = '339478d0-0b95-4a94-be63-d5be05dfeb1c'
- self.service_mock.list.return_value = [mock.Mock(id=service_id)]
+ self.sdk_client.services.return_value = [mock.Mock(id=service_id)]
result = self.cmd.take_action(parsed_args)
- self.service_mock.enable.assert_called_once_with(service_id)
- self.service_mock.force_down.assert_called_once_with(service_id, False)
+ self.sdk_client.enable_service.assert_called_once_with(
+ service_id,
+ self.service.host,
+ self.service.binary
+ )
+ self.sdk_client.update_service_forced_down.assert_called_once_with(
+ service_id,
+ self.service.host,
+ self.service.binary,
+ False
+ )
self.assertIsNone(result)
def test_service_set_find_service_by_host_and_binary_no_results(self):
# Tests that no compute services are found by host and binary.
- self.service_mock.list.return_value = []
+ self.sdk_client.services.return_value = []
ex = self.assertRaises(exceptions.CommandError,
self.cmd._find_service_by_host_and_binary,
- self.service_mock, 'fake-host', 'nova-compute')
+ self.sdk_client, 'fake-host', 'nova-compute')
self.assertIn('Compute service for host "fake-host" and binary '
'"nova-compute" not found.', str(ex))
def test_service_set_find_service_by_host_and_binary_many_results(self):
# Tests that more than one compute service is found by host and binary.
- self.service_mock.list.return_value = [mock.Mock(), mock.Mock()]
+ self.sdk_client.services.return_value = [mock.Mock(), mock.Mock()]
ex = self.assertRaises(exceptions.CommandError,
self.cmd._find_service_by_host_and_binary,
- self.service_mock, 'fake-host', 'nova-compute')
+ self.sdk_client, 'fake-host', 'nova-compute')
self.assertIn('Multiple compute services found for host "fake-host" '
'and binary "nova-compute". Unable to proceed.',
str(ex))
diff --git a/openstackclient/tests/unit/network/v2/fakes.py b/openstackclient/tests/unit/network/v2/fakes.py
index ab77d719..2f839d16 100644
--- a/openstackclient/tests/unit/network/v2/fakes.py
+++ b/openstackclient/tests/unit/network/v2/fakes.py
@@ -18,6 +18,9 @@ from random import randint
from unittest import mock
import uuid
+from openstack.network.v2 import local_ip as _local_ip
+from openstack.network.v2 import local_ip_association as _local_ip_association
+
from openstackclient.tests.unit import fakes
from openstackclient.tests.unit.identity.v3 import fakes as identity_fakes_v3
from openstackclient.tests.unit import utils
@@ -102,8 +105,9 @@ class FakeAddressGroup(object):
'name': 'address-group-name-' + uuid.uuid4().hex,
'description': 'address-group-description-' + uuid.uuid4().hex,
'id': 'address-group-id-' + uuid.uuid4().hex,
- 'tenant_id': 'project-id-' + uuid.uuid4().hex,
+ 'project_id': 'project-id-' + uuid.uuid4().hex,
'addresses': ['10.0.0.1/32'],
+ 'location': 'MUNCHMUNCHMUNCH',
}
# Overwrite default attributes.
@@ -113,9 +117,6 @@ class FakeAddressGroup(object):
info=copy.deepcopy(address_group_attrs),
loaded=True)
- # Set attributes with special mapping in OpenStack SDK.
- address_group.project_id = address_group_attrs['tenant_id']
-
return address_group
@staticmethod
@@ -174,9 +175,10 @@ class FakeAddressScope(object):
address_scope_attrs = {
'name': 'address-scope-name-' + uuid.uuid4().hex,
'id': 'address-scope-id-' + uuid.uuid4().hex,
- 'tenant_id': 'project-id-' + uuid.uuid4().hex,
+ 'project_id': 'project-id-' + uuid.uuid4().hex,
'shared': False,
'ip_version': 4,
+ 'location': 'MUNCHMUNCHMUNCH',
}
# Overwrite default attributes.
@@ -188,7 +190,6 @@ class FakeAddressScope(object):
# Set attributes with special mapping in OpenStack SDK.
address_scope.is_shared = address_scope_attrs['shared']
- address_scope.project_id = address_scope_attrs['tenant_id']
return address_scope
@@ -239,7 +240,7 @@ class FakeAutoAllocatedTopology(object):
auto_allocated_topology_attrs = {
'id': 'network-id-' + uuid.uuid4().hex,
- 'tenant_id': 'project-id-' + uuid.uuid4().hex,
+ 'project_id': 'project-id-' + uuid.uuid4().hex,
}
auto_allocated_topology_attrs.update(attrs)
@@ -248,10 +249,6 @@ class FakeAutoAllocatedTopology(object):
info=copy.deepcopy(auto_allocated_topology_attrs),
loaded=True)
- auto_allocated_topology.project_id = auto_allocated_topology_attrs[
- 'tenant_id'
- ]
-
return auto_allocated_topology
@@ -322,17 +319,17 @@ class FakeIPAvailability(object):
network_ip_attrs = {
'network_id': 'network-id-' + uuid.uuid4().hex,
'network_name': 'network-name-' + uuid.uuid4().hex,
- 'tenant_id': '',
+ 'project_id': '',
'subnet_ip_availability': [],
'total_ips': 254,
'used_ips': 6,
+ 'location': 'MUNCHMUNCHMUNCH',
}
network_ip_attrs.update(attrs)
network_ip_availability = fakes.FakeResource(
info=copy.deepcopy(network_ip_attrs),
loaded=True)
- network_ip_availability.project_id = network_ip_attrs['tenant_id']
return network_ip_availability
@@ -409,7 +406,7 @@ class FakeNetwork(object):
'description': 'network-description-' + uuid.uuid4().hex,
'dns_domain': 'example.org.',
'mtu': '1350',
- 'tenant_id': 'project-id-' + uuid.uuid4().hex,
+ 'project_id': 'project-id-' + uuid.uuid4().hex,
'admin_state_up': True,
'shared': False,
'subnets': ['a', 'b'],
@@ -425,6 +422,7 @@ class FakeNetwork(object):
'ipv4_address_scope': 'ipv4' + uuid.uuid4().hex,
'ipv6_address_scope': 'ipv6' + uuid.uuid4().hex,
'tags': [],
+ 'location': 'MUNCHMUNCHMUNCH',
}
# Overwrite default attributes.
@@ -434,7 +432,6 @@ class FakeNetwork(object):
loaded=True)
# Set attributes with special mapping in OpenStack SDK.
- network.project_id = network_attrs['tenant_id']
network.is_router_external = network_attrs['router:external']
network.is_admin_state_up = network_attrs['admin_state_up']
network.is_port_security_enabled = \
@@ -513,7 +510,8 @@ class FakeNetworkFlavor(object):
'id': 'network-flavor-id-' + fake_uuid,
'name': 'network-flavor-name-' + fake_uuid,
'service_type': 'vpn',
- 'tenant_id': 'project-id-' + uuid.uuid4().hex,
+ 'project_id': 'project-id-' + uuid.uuid4().hex,
+ 'location': 'MUNCHMUNCHMUNCH',
}
# Overwrite default attributes.
@@ -524,7 +522,6 @@ class FakeNetworkFlavor(object):
loaded=True
)
- network_flavor.project_id = network_flavor_attrs['tenant_id']
network_flavor.is_enabled = network_flavor_attrs['enabled']
return network_flavor
@@ -579,6 +576,7 @@ class FakeNetworkSegment(object):
'network_type': 'vlan',
'physical_network': 'physical-network-name-' + fake_uuid,
'segmentation_id': 1024,
+ 'location': 'MUNCHMUNCHMUNCH',
}
# Overwrite default attributes.
@@ -639,6 +637,7 @@ class FakeNetworkSegmentRange(object):
'used': {104: '3312e4ba67864b2eb53f3f41432f8efc',
106: '3312e4ba67864b2eb53f3f41432f8efc'},
'available': [100, 101, 102, 103, 105],
+ 'location': 'MUNCHMUNCHMUNCH',
}
# Overwrite default attributes.
@@ -712,11 +711,12 @@ class FakePort(object):
'port_security_enabled': True,
'security_group_ids': [],
'status': 'ACTIVE',
- 'tenant_id': 'project-id-' + uuid.uuid4().hex,
+ 'project_id': 'project-id-' + uuid.uuid4().hex,
'qos_network_policy_id': 'qos-policy-id-' + uuid.uuid4().hex,
'qos_policy_id': 'qos-policy-id-' + uuid.uuid4().hex,
'tags': [],
'propagate_uplink_status': False,
+ 'location': 'MUNCHMUNCHMUNCH',
}
# Overwrite default attributes.
@@ -733,11 +733,6 @@ class FakePort(object):
port.binding_vnic_type = port_attrs['binding:vnic_type']
port.is_admin_state_up = port_attrs['admin_state_up']
port.is_port_security_enabled = port_attrs['port_security_enabled']
- port.project_id = port_attrs['tenant_id']
- port.security_group_ids = port_attrs['security_group_ids']
- port.qos_policy_id = port_attrs['qos_policy_id']
- port.propagate_uplink_status = port_attrs[
- 'propagate_uplink_status']
return port
@@ -802,6 +797,7 @@ class FakeNetworkAgent(object):
'admin_state_up': True,
'binary': 'binary-' + uuid.uuid4().hex,
'configurations': {'subnet': 2, 'networks': 1},
+ 'location': 'MUNCHMUNCHMUNCH',
}
agent_attrs.update(attrs)
agent = fakes.FakeResource(info=copy.deepcopy(agent_attrs),
@@ -858,7 +854,7 @@ class FakeNetworkRBAC(object):
A dictionary with all attributes
:return:
A FakeResource object, with id, action, target_tenant,
- tenant_id, type
+ project_id, type
"""
attrs = attrs or {}
@@ -869,13 +865,13 @@ class FakeNetworkRBAC(object):
'object_id': 'object-id-' + uuid.uuid4().hex,
'action': 'access_as_shared',
'target_tenant': 'target-tenant-' + uuid.uuid4().hex,
- 'tenant_id': 'tenant-id-' + uuid.uuid4().hex,
+ 'project_id': 'project-id-' + uuid.uuid4().hex,
+ 'location': 'MUNCHMUNCHMUNCH',
}
rbac_attrs.update(attrs)
rbac = fakes.FakeResource(info=copy.deepcopy(rbac_attrs),
loaded=True)
# Set attributes with special mapping in OpenStack SDK.
- rbac.project_id = rbac_attrs['tenant_id']
rbac.target_project_id = rbac_attrs['target_tenant']
return rbac
@@ -928,10 +924,11 @@ class FakeNetworkFlavorProfile(object):
flavor_profile_attrs = {
'id': 'flavor-profile-id' + uuid.uuid4().hex,
'description': 'flavor-profile-description-' + uuid.uuid4().hex,
- 'tenant_id': 'project-id-' + uuid.uuid4().hex,
+ 'project_id': 'project-id-' + uuid.uuid4().hex,
'driver': 'driver-' + uuid.uuid4().hex,
'metainfo': 'metainfo-' + uuid.uuid4().hex,
- 'enabled': True
+ 'enabled': True,
+ 'location': 'MUNCHMUNCHMUNCH',
}
flavor_profile_attrs.update(attrs)
@@ -940,7 +937,6 @@ class FakeNetworkFlavorProfile(object):
info=copy.deepcopy(flavor_profile_attrs),
loaded=True)
- flavor_profile.project_id = flavor_profile_attrs['tenant_id']
flavor_profile.is_enabled = flavor_profile_attrs['enabled']
return flavor_profile
@@ -986,10 +982,11 @@ class FakeNetworkQosPolicy(object):
'name': 'qos-policy-name-' + uuid.uuid4().hex,
'id': qos_id,
'is_default': False,
- 'tenant_id': 'project-id-' + uuid.uuid4().hex,
+ 'project_id': 'project-id-' + uuid.uuid4().hex,
'shared': False,
'description': 'qos-policy-description-' + uuid.uuid4().hex,
'rules': rules,
+ 'location': 'MUNCHMUNCHMUNCH',
}
# Overwrite default attributes.
@@ -1001,7 +998,6 @@ class FakeNetworkQosPolicy(object):
# Set attributes with special mapping in OpenStack SDK.
qos_policy.is_shared = qos_policy_attrs['shared']
- qos_policy.project_id = qos_policy_attrs['tenant_id']
return qos_policy
@@ -1062,17 +1058,15 @@ class FakeNetworkSecGroup(object):
security_group_attrs = {
'name': 'security-group-name-' + uuid.uuid4().hex,
'id': sg_id,
- 'tenant_id': 'project-id-' + uuid.uuid4().hex,
- 'description': 'security-group-description-' + uuid.uuid4().hex
+ 'project_id': 'project-id-' + uuid.uuid4().hex,
+ 'description': 'security-group-description-' + uuid.uuid4().hex,
+ 'location': 'MUNCHMUNCHMUNCH',
}
security_group = fakes.FakeResource(
info=copy.deepcopy(security_group_attrs),
loaded=True)
- # Set attributes with special mapping in OpenStack SDK.
- security_group.project_id = security_group_attrs['tenant_id']
-
return security_group
@@ -1095,8 +1089,9 @@ class FakeNetworkQosRule(object):
qos_rule_attrs = {
'id': 'qos-rule-id-' + uuid.uuid4().hex,
'qos_policy_id': 'qos-policy-id-' + uuid.uuid4().hex,
- 'tenant_id': 'project-id-' + uuid.uuid4().hex,
+ 'project_id': 'project-id-' + uuid.uuid4().hex,
'type': type,
+ 'location': 'MUNCHMUNCHMUNCH',
}
if type == RULE_TYPE_BANDWIDTH_LIMIT:
qos_rule_attrs['max_kbps'] = randint(1, 10000)
@@ -1114,9 +1109,6 @@ class FakeNetworkQosRule(object):
qos_rule = fakes.FakeResource(info=copy.deepcopy(qos_rule_attrs),
loaded=True)
- # Set attributes with special mapping in OpenStack SDK.
- qos_rule.project_id = qos_rule['tenant_id']
-
return qos_rule
@staticmethod
@@ -1172,6 +1164,7 @@ class FakeNetworkQosRuleType(object):
# Set default attributes.
qos_rule_type_attrs = {
'type': 'rule-type-' + uuid.uuid4().hex,
+ 'location': 'MUNCHMUNCHMUNCH',
}
# Overwrite default attributes.
@@ -1211,7 +1204,7 @@ class FakeRouter(object):
A dictionary with all attributes
:return:
A FakeResource object, with id, name, admin_state_up,
- status, tenant_id
+ status, project_id
"""
attrs = attrs or {}
@@ -1224,12 +1217,13 @@ class FakeRouter(object):
'description': 'router-description-' + uuid.uuid4().hex,
'distributed': False,
'ha': False,
- 'tenant_id': 'project-id-' + uuid.uuid4().hex,
+ 'project_id': 'project-id-' + uuid.uuid4().hex,
'routes': [],
'external_gateway_info': {},
'availability_zone_hints': [],
'availability_zones': [],
'tags': [],
+ 'location': 'MUNCHMUNCHMUNCH',
}
# Overwrite default attributes.
@@ -1239,7 +1233,6 @@ class FakeRouter(object):
loaded=True)
# Set attributes with special mapping in OpenStack SDK.
- router.project_id = router_attrs['tenant_id']
router.is_admin_state_up = router_attrs['admin_state_up']
router.is_distributed = router_attrs['distributed']
router.is_ha = router_attrs['ha']
@@ -1305,7 +1298,8 @@ class FakeSecurityGroup(object):
'stateful': True,
'project_id': 'project-id-' + uuid.uuid4().hex,
'security_group_rules': [],
- 'tags': []
+ 'tags': [],
+ 'location': 'MUNCHMUNCHMUNCH',
}
# Overwrite default attributes.
@@ -1315,9 +1309,6 @@ class FakeSecurityGroup(object):
info=copy.deepcopy(security_group_attrs),
loaded=True)
- # Set attributes with special mapping in OpenStack SDK.
- security_group.project_id = security_group_attrs['project_id']
-
return security_group
@staticmethod
@@ -1386,7 +1377,8 @@ class FakeSecurityGroupRule(object):
'remote_address_group_id': None,
'remote_ip_prefix': '0.0.0.0/0',
'security_group_id': 'security-group-id-' + uuid.uuid4().hex,
- 'tenant_id': 'project-id-' + uuid.uuid4().hex,
+ 'project_id': 'project-id-' + uuid.uuid4().hex,
+ 'location': 'MUNCHMUNCHMUNCH',
}
# Overwrite default attributes.
@@ -1396,9 +1388,6 @@ class FakeSecurityGroupRule(object):
info=copy.deepcopy(security_group_rule_attrs),
loaded=True)
- # Set attributes with special mapping in OpenStack SDK.
- security_group_rule.project_id = security_group_rule_attrs['tenant_id']
-
return security_group_rule
@staticmethod
@@ -1461,7 +1450,7 @@ class FakeSubnet(object):
'name': 'subnet-name-' + uuid.uuid4().hex,
'network_id': 'network-id-' + uuid.uuid4().hex,
'cidr': '10.10.10.0/24',
- 'tenant_id': project_id,
+ 'project_id': project_id,
'enable_dhcp': True,
'dns_nameservers': [],
'allocation_pools': [],
@@ -1475,6 +1464,7 @@ class FakeSubnet(object):
'subnetpool_id': None,
'description': 'subnet-description-' + uuid.uuid4().hex,
'tags': [],
+ 'location': 'MUNCHMUNCHMUNCH',
}
# Overwrite default attributes.
@@ -1486,7 +1476,6 @@ class FakeSubnet(object):
# Set attributes with special mappings in OpenStack SDK.
subnet.is_dhcp_enabled = subnet_attrs['enable_dhcp']
subnet.subnet_pool_id = subnet_attrs['subnetpool_id']
- subnet.project_id = subnet_attrs['tenant_id']
return subnet
@@ -1552,10 +1541,11 @@ class FakeFloatingIP(object):
'floating_network_id': 'network-id-' + uuid.uuid4().hex,
'router_id': 'router-id-' + uuid.uuid4().hex,
'port_id': 'port-id-' + uuid.uuid4().hex,
- 'tenant_id': 'project-id-' + uuid.uuid4().hex,
+ 'project_id': 'project-id-' + uuid.uuid4().hex,
'description': 'floating-ip-description-' + uuid.uuid4().hex,
'qos_policy_id': 'qos-policy-id-' + uuid.uuid4().hex,
'tags': [],
+ 'location': 'MUNCHMUNCHMUNCH',
}
# Overwrite default attributes.
@@ -1566,9 +1556,6 @@ class FakeFloatingIP(object):
loaded=True
)
- # Set attributes with special mappings in OpenStack SDK.
- floating_ip.project_id = floating_ip_attrs['tenant_id']
-
return floating_ip
@staticmethod
@@ -1619,8 +1606,9 @@ class FakeNetworkMeter(object):
'id': 'meter-id-' + uuid.uuid4().hex,
'name': 'meter-name-' + uuid.uuid4().hex,
'description': 'meter-description-' + uuid.uuid4().hex,
- 'tenant_id': 'project-id-' + uuid.uuid4().hex,
- 'shared': False
+ 'project_id': 'project-id-' + uuid.uuid4().hex,
+ 'shared': False,
+ 'location': 'MUNCHMUNCHMUNCH',
}
meter_attrs.update(attrs)
@@ -1629,8 +1617,6 @@ class FakeNetworkMeter(object):
info=copy.deepcopy(meter_attrs),
loaded=True)
- meter.project_id = meter_attrs['tenant_id']
-
return meter
@staticmethod
@@ -1668,7 +1654,8 @@ class FakeNetworkMeterRule(object):
'remote_ip_prefix': '10.0.0.0/24',
'source_ip_prefix': '8.8.8.8/32',
'destination_ip_prefix': '10.0.0.0/24',
- 'tenant_id': 'project-id-' + uuid.uuid4().hex,
+ 'project_id': 'project-id-' + uuid.uuid4().hex,
+ 'location': 'MUNCHMUNCHMUNCH',
}
meter_rule_attrs.update(attrs)
@@ -1677,8 +1664,6 @@ class FakeNetworkMeterRule(object):
info=copy.deepcopy(meter_rule_attrs),
loaded=True)
- meter_rule.project_id = meter_rule_attrs['tenant_id']
-
return meter_rule
@staticmethod
@@ -1721,7 +1706,7 @@ class FakeSubnetPool(object):
'prefixes': ['10.0.0.0/24', '10.1.0.0/24'],
'default_prefixlen': '8',
'address_scope_id': 'address-scope-id-' + uuid.uuid4().hex,
- 'tenant_id': 'project-id-' + uuid.uuid4().hex,
+ 'project_id': 'project-id-' + uuid.uuid4().hex,
'is_default': False,
'shared': False,
'max_prefixlen': '32',
@@ -1730,6 +1715,7 @@ class FakeSubnetPool(object):
'ip_version': '4',
'description': 'subnet-pool-description-' + uuid.uuid4().hex,
'tags': [],
+ 'location': 'MUNCHMUNCHMUNCH',
}
# Overwrite default attributes.
@@ -1746,7 +1732,6 @@ class FakeSubnetPool(object):
subnet_pool.is_shared = subnet_pool_attrs['shared']
subnet_pool.maximum_prefix_length = subnet_pool_attrs['max_prefixlen']
subnet_pool.minimum_prefix_length = subnet_pool_attrs['min_prefixlen']
- subnet_pool.project_id = subnet_pool_attrs['tenant_id']
return subnet_pool
@@ -1801,6 +1786,7 @@ class FakeNetworkServiceProvider(object):
'name': 'provider-name-' + uuid.uuid4().hex,
'service_type': 'service-type-' + uuid.uuid4().hex,
'default': False,
+ 'location': 'MUNCHMUNCHMUNCH',
}
service_provider.update(attrs)
@@ -1923,6 +1909,7 @@ class FakeFloatingIPPortForwarding(object):
'external_port': randint(1, 65535),
'protocol': 'tcp',
'description': 'some description',
+ 'location': 'MUNCHMUNCHMUNCH',
}
# Overwrite default attributes.
@@ -1998,6 +1985,7 @@ class FakeL3ConntrackHelper(object):
'helper': 'tftp',
'protocol': 'tcp',
'port': randint(1, 65535),
+ 'location': 'MUNCHMUNCHMUNCH',
}
# Overwrite default attributes.
@@ -2048,3 +2036,138 @@ class FakeL3ConntrackHelper(object):
)
return mock.Mock(side_effect=ct_helpers)
+
+
+def create_one_local_ip(attrs=None):
+ """Create a fake local ip.
+
+ :param Dictionary attrs:
+ A dictionary with all attributes
+ :return:
+ A FakeResource object with name, id, etc.
+ """
+ attrs = attrs or {}
+
+ # Set default attributes.
+ local_ip_attrs = {
+ 'created_at': '2021-11-29T10:10:23.000000',
+ 'name': 'local-ip-name-' + uuid.uuid4().hex,
+ 'description': 'local-ip-description-' + uuid.uuid4().hex,
+ 'id': 'local-ip-id-' + uuid.uuid4().hex,
+ 'project_id': 'project-id-' + uuid.uuid4().hex,
+ 'local_port_id': 'local_port_id-' + uuid.uuid4().hex,
+ 'network_id': 'network_id-' + uuid.uuid4().hex,
+ 'local_ip_address': '10.0.0.1',
+ 'ip_mode': 'translate',
+ 'revision_number': 'local-ip-revision-number-' + uuid.uuid4().hex,
+ 'updated_at': '2021-11-29T10:10:25.000000',
+ }
+
+ # Overwrite default attributes.
+ local_ip_attrs.update(attrs)
+
+ local_ip = _local_ip.LocalIP(**local_ip_attrs)
+
+ return local_ip
+
+
+def create_local_ips(attrs=None, count=2):
+ """Create multiple fake local ips.
+
+ :param Dictionary attrs:
+ A dictionary with all attributes
+ :param int count:
+ The number of local ips to fake
+ :return:
+ A list of FakeResource objects faking the local ips
+ """
+ local_ips = []
+ for i in range(0, count):
+ local_ips.append(create_one_local_ip(attrs))
+
+ return local_ips
+
+
+def get_local_ips(local_ips=None, count=2):
+ """Get an iterable Mock object with a list of faked local ips.
+
+ If local ip list is provided, then initialize the Mock object
+ with the list. Otherwise create one.
+
+ :param List local_ips:
+ A list of FakeResource objects faking local ips
+ :param int count:
+ The number of local ips to fake
+ :return:
+ An iterable Mock object with side_effect set to a list of faked
+ local ips
+ """
+ if local_ips is None:
+ local_ips = create_local_ips(count)
+ return mock.Mock(side_effect=local_ips)
+
+
+def create_one_local_ip_association(attrs=None):
+ """Create a fake local ip association.
+
+ :param Dictionary attrs:
+ A dictionary with all attributes
+ :return:
+ A FakeResource object with local_ip_id, local_ip_address, etc.
+ """
+ attrs = attrs or {}
+
+ # Set default attributes.
+ local_ip_association_attrs = {
+ 'local_ip_id': 'local-ip-id-' + uuid.uuid4().hex,
+ 'local_ip_address': '172.24.4.228',
+ 'fixed_port_id': 'fixed-port-id-' + uuid.uuid4().hex,
+ 'fixed_ip': '10.0.0.5',
+ 'host': 'host-' + uuid.uuid4().hex,
+ }
+
+ # Overwrite default attributes.
+ local_ip_association_attrs.update(attrs)
+
+ local_ip_association = (
+ _local_ip_association.LocalIPAssociation(
+ **local_ip_association_attrs))
+
+ return local_ip_association
+
+
+def create_local_ip_associations(attrs=None, count=2):
+ """Create multiple fake local ip associations.
+
+ :param Dictionary attrs:
+ A dictionary with all attributes
+ :param int count:
+ The number of address groups to fake
+ :return:
+ A list of FakeResource objects faking the local ip associations
+ """
+ local_ip_associations = []
+ for i in range(0, count):
+ local_ip_associations.append(create_one_local_ip_association(attrs))
+
+ return local_ip_associations
+
+
+def get_local_ip_associations(local_ip_associations=None, count=2):
+ """Get a list of faked local ip associations
+
+ If local ip association list is provided, then initialize
+ the Mock object with the list. Otherwise create one.
+
+ :param List local_ip_associations:
+ A list of FakeResource objects faking local ip associations
+ :param int count:
+ The number of local ip associations to fake
+ :return:
+ An iterable Mock object with side_effect set to a list of faked
+ local ip associations
+ """
+ if local_ip_associations is None:
+ local_ip_associations = create_local_ip_associations(count)
+
+ return mock.Mock(side_effect=local_ip_associations)
diff --git a/openstackclient/tests/unit/network/v2/test_address_group.py b/openstackclient/tests/unit/network/v2/test_address_group.py
index a5ee83cb..703ab74d 100644
--- a/openstackclient/tests/unit/network/v2/test_address_group.py
+++ b/openstackclient/tests/unit/network/v2/test_address_group.py
@@ -43,7 +43,7 @@ class TestCreateAddressGroup(TestAddressGroup):
new_address_group = (
network_fakes.FakeAddressGroup.create_one_address_group(
attrs={
- 'tenant_id': project.id,
+ 'project_id': project.id,
}
))
columns = (
@@ -122,7 +122,7 @@ class TestCreateAddressGroup(TestAddressGroup):
self.network.create_address_group.assert_called_once_with(**{
'addresses': ['10.0.0.1/32'],
- 'tenant_id': self.project.id,
+ 'project_id': self.project.id,
'name': self.new_address_group.name,
'description': self.new_address_group.description,
})
diff --git a/openstackclient/tests/unit/network/v2/test_address_scope.py b/openstackclient/tests/unit/network/v2/test_address_scope.py
index 17f13e83..7e7c4215 100644
--- a/openstackclient/tests/unit/network/v2/test_address_scope.py
+++ b/openstackclient/tests/unit/network/v2/test_address_scope.py
@@ -43,7 +43,7 @@ class TestCreateAddressScope(TestAddressScope):
new_address_scope = (
network_fakes.FakeAddressScope.create_one_address_scope(
attrs={
- 'tenant_id': project.id,
+ 'project_id': project.id,
}
))
columns = (
@@ -122,7 +122,7 @@ class TestCreateAddressScope(TestAddressScope):
self.network.create_address_scope.assert_called_once_with(**{
'ip_version': self.new_address_scope.ip_version,
'shared': True,
- 'tenant_id': self.project.id,
+ 'project_id': self.project.id,
'name': self.new_address_scope.name,
})
self.assertEqual(self.columns, columns)
@@ -318,7 +318,7 @@ class TestListAddressScope(TestAddressScope):
columns, data = self.cmd.take_action(parsed_args)
self.network.address_scopes.assert_called_once_with(
- **{'tenant_id': project.id, 'project_id': project.id})
+ **{'project_id': project.id})
self.assertEqual(self.columns, columns)
self.assertEqual(self.data, list(data))
@@ -335,7 +335,7 @@ class TestListAddressScope(TestAddressScope):
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
columns, data = self.cmd.take_action(parsed_args)
- filters = {'tenant_id': project.id, 'project_id': project.id}
+ filters = {'project_id': project.id}
self.network.address_scopes.assert_called_once_with(**filters)
self.assertEqual(self.columns, columns)
diff --git a/openstackclient/tests/unit/network/v2/test_floating_ip_network.py b/openstackclient/tests/unit/network/v2/test_floating_ip_network.py
index dbcd5c97..5b5c83a5 100644
--- a/openstackclient/tests/unit/network/v2/test_floating_ip_network.py
+++ b/openstackclient/tests/unit/network/v2/test_floating_ip_network.py
@@ -179,7 +179,7 @@ class TestCreateFloatingIPNetwork(TestFloatingIPNetwork):
self.network.create_ip.assert_called_once_with(**{
'floating_network_id': self.floating_ip.floating_network_id,
- 'tenant_id': project.id,
+ 'project_id': project.id,
})
self.assertEqual(self.columns, columns)
self.assertEqual(self.data, data)
@@ -205,7 +205,7 @@ class TestCreateFloatingIPNetwork(TestFloatingIPNetwork):
self.network.create_ip.assert_called_once_with(**{
'floating_network_id': self.floating_ip.floating_network_id,
- 'tenant_id': project.id,
+ 'project_id': project.id,
})
self.assertEqual(self.columns, columns)
self.assertEqual(self.data, data)
@@ -414,7 +414,7 @@ class TestListFloatingIPNetwork(TestFloatingIPNetwork):
ip.fixed_ip_address,
ip.port_id,
ip.floating_network_id,
- ip.tenant_id,
+ ip.project_id,
))
data_long.append((
ip.id,
@@ -422,7 +422,7 @@ class TestListFloatingIPNetwork(TestFloatingIPNetwork):
ip.fixed_ip_address,
ip.port_id,
ip.floating_network_id,
- ip.tenant_id,
+ ip.project_id,
ip.router_id,
ip.status,
ip.description,
@@ -563,8 +563,7 @@ class TestListFloatingIPNetwork(TestFloatingIPNetwork):
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
columns, data = self.cmd.take_action(parsed_args)
- filters = {'tenant_id': project.id,
- 'project_id': project.id, }
+ filters = {'project_id': project.id}
self.network.ips.assert_called_once_with(**filters)
@@ -584,8 +583,7 @@ class TestListFloatingIPNetwork(TestFloatingIPNetwork):
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
columns, data = self.cmd.take_action(parsed_args)
- filters = {'tenant_id': project.id,
- 'project_id': project.id, }
+ filters = {'project_id': project.id}
self.network.ips.assert_called_once_with(**filters)
self.assertEqual(self.columns, columns)
diff --git a/openstackclient/tests/unit/network/v2/test_floating_ip_port_forwarding.py b/openstackclient/tests/unit/network/v2/test_floating_ip_port_forwarding.py
index 1028c18a..7b9e3aa6 100644
--- a/openstackclient/tests/unit/network/v2/test_floating_ip_port_forwarding.py
+++ b/openstackclient/tests/unit/network/v2/test_floating_ip_port_forwarding.py
@@ -39,7 +39,6 @@ class TestFloatingIPPortForwarding(network_fakes.TestNetworkV2):
class TestCreateFloatingIPPortForwarding(TestFloatingIPPortForwarding):
def setUp(self):
- project_id = ''
super(TestCreateFloatingIPPortForwarding, self).setUp()
self.new_port_forwarding = (
network_fakes.FakeFloatingIPPortForwarding.
@@ -69,7 +68,6 @@ class TestCreateFloatingIPPortForwarding(TestFloatingIPPortForwarding):
'internal_ip_address',
'internal_port',
'internal_port_id',
- 'project_id',
'protocol'
)
@@ -81,7 +79,6 @@ class TestCreateFloatingIPPortForwarding(TestFloatingIPPortForwarding):
self.new_port_forwarding.internal_ip_address,
self.new_port_forwarding.internal_port,
self.new_port_forwarding.internal_port_id,
- project_id,
self.new_port_forwarding.protocol,
)
@@ -446,12 +443,10 @@ class TestShowFloatingIPPortForwarding(TestFloatingIPPortForwarding):
'internal_ip_address',
'internal_port',
'internal_port_id',
- 'project_id',
'protocol',
)
def setUp(self):
- project_id = ''
super(TestShowFloatingIPPortForwarding, self).setUp()
self._port_forwarding = (
network_fakes.FakeFloatingIPPortForwarding.
@@ -469,7 +464,6 @@ class TestShowFloatingIPPortForwarding(TestFloatingIPPortForwarding):
self._port_forwarding.internal_ip_address,
self._port_forwarding.internal_port,
self._port_forwarding.internal_port_id,
- project_id,
self._port_forwarding.protocol,
)
self.network.find_floating_ip_port_forwarding = mock.Mock(
diff --git a/openstackclient/tests/unit/network/v2/test_ip_availability.py b/openstackclient/tests/unit/network/v2/test_ip_availability.py
index a722a023..880cf581 100644
--- a/openstackclient/tests/unit/network/v2/test_ip_availability.py
+++ b/openstackclient/tests/unit/network/v2/test_ip_availability.py
@@ -106,8 +106,7 @@ class TestListIPAvailability(TestIPAvailability):
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
columns, data = self.cmd.take_action(parsed_args)
- filters = {'tenant_id': self.project.id,
- 'project_id': self.project.id,
+ filters = {'project_id': self.project.id,
'ip_version': 4}
self.network.network_ip_availabilities.assert_called_once_with(
@@ -134,7 +133,7 @@ class TestShowIPAvailability(TestIPAvailability):
data = (
_ip_availability.network_id,
_ip_availability.network_name,
- _ip_availability.tenant_id,
+ _ip_availability.project_id,
format_columns.ListDictColumn(
_ip_availability.subnet_ip_availability),
_ip_availability.total_ips,
diff --git a/openstackclient/tests/unit/network/v2/test_local_ip.py b/openstackclient/tests/unit/network/v2/test_local_ip.py
new file mode 100644
index 00000000..17e8dcd1
--- /dev/null
+++ b/openstackclient/tests/unit/network/v2/test_local_ip.py
@@ -0,0 +1,480 @@
+# Copyright 2021 Huawei, Inc. 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.
+#
+
+from unittest import mock
+from unittest.mock import call
+
+from osc_lib import exceptions
+
+from openstackclient.network.v2 import local_ip
+from openstackclient.tests.unit.identity.v3 import fakes as identity_fakes_v3
+from openstackclient.tests.unit.network.v2 import fakes as network_fakes
+from openstackclient.tests.unit import utils as tests_utils
+
+
+class TestLocalIP(network_fakes.TestNetworkV2):
+
+ def setUp(self):
+ super().setUp()
+
+ # Get a shortcut to the network client
+ self.network = self.app.client_manager.network
+ # Get a shortcut to the ProjectManager Mock
+ self.projects_mock = self.app.client_manager.identity.projects
+ # Get a shortcut to the DomainManager Mock
+ self.domains_mock = self.app.client_manager.identity.domains
+
+
+class TestCreateLocalIP(TestLocalIP):
+ project = identity_fakes_v3.FakeProject.create_one_project()
+ domain = identity_fakes_v3.FakeDomain.create_one_domain()
+ local_ip_network = network_fakes.FakeNetwork.create_one_network()
+ port = network_fakes.FakePort.create_one_port()
+ # The new local ip created.
+ new_local_ip = network_fakes.create_one_local_ip(
+ attrs={'project_id': project.id,
+ 'network_id': local_ip_network.id,
+ 'local_port_id': port.id})
+
+ columns = (
+ 'created_at',
+ 'description',
+ 'id',
+ 'name',
+ 'project_id',
+ 'local_port_id',
+ 'network_id',
+ 'local_ip_address',
+ 'ip_mode',
+ 'revision_number',
+ 'updated_at',
+ )
+ data = (
+ new_local_ip.created_at,
+ new_local_ip.description,
+ new_local_ip.id,
+ new_local_ip.name,
+ new_local_ip.project_id,
+ new_local_ip.local_port_id,
+ new_local_ip.network_id,
+ new_local_ip.local_ip_address,
+ new_local_ip.ip_mode,
+ new_local_ip.revision_number,
+ new_local_ip.updated_at,
+ )
+
+ def setUp(self):
+ super().setUp()
+ self.network.create_local_ip = mock.Mock(
+ return_value=self.new_local_ip)
+ self.network.find_network = mock.Mock(
+ return_value=self.local_ip_network)
+ self.network.find_port = mock.Mock(
+ return_value=self.port)
+
+ # Get the command object to test
+ self.cmd = local_ip.CreateLocalIP(self.app, self.namespace)
+
+ self.projects_mock.get.return_value = self.project
+ self.domains_mock.get.return_value = self.domain
+
+ def test_create_no_options(self):
+ parsed_args = self.check_parser(self.cmd, [], [])
+ columns, data = (self.cmd.take_action(parsed_args))
+
+ self.network.create_local_ip.assert_called_once_with(**{})
+ self.assertEqual(set(self.columns), set(columns))
+ self.assertItemsEqual(self.data, data)
+
+ def test_create_all_options(self):
+ arglist = [
+ '--project-domain', self.domain.name,
+ '--description', self.new_local_ip.description,
+ '--name', self.new_local_ip.name,
+ '--network', self.new_local_ip.network_id,
+ '--local-port', self.new_local_ip.local_port_id,
+ '--local-ip-address', '10.0.0.1',
+ '--ip-mode', self.new_local_ip.ip_mode,
+ ]
+ verifylist = [
+ ('project_domain', self.domain.name),
+ ('description', self.new_local_ip.description),
+ ('name', self.new_local_ip.name),
+ ('network', self.new_local_ip.network_id),
+ ('local_port', self.new_local_ip.local_port_id),
+ ('local_ip_address', '10.0.0.1'),
+ ('ip_mode', self.new_local_ip.ip_mode),
+ ]
+
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+ columns, data = (self.cmd.take_action(parsed_args))
+
+ self.network.create_local_ip.assert_called_once_with(**{
+ 'name': self.new_local_ip.name,
+ 'description': self.new_local_ip.description,
+ 'network_id': self.new_local_ip.network_id,
+ 'local_port_id': self.new_local_ip.local_port_id,
+ 'local_ip_address': '10.0.0.1',
+ 'ip_mode': self.new_local_ip.ip_mode,
+ })
+ self.assertEqual(set(self.columns), set(columns))
+ self.assertItemsEqual(self.data, data)
+
+
+class TestDeleteLocalIP(TestLocalIP):
+ # The local ip to delete.
+ _local_ips = network_fakes.create_local_ips(count=2)
+
+ def setUp(self):
+ super().setUp()
+ self.network.delete_local_ip = mock.Mock(return_value=None)
+ self.network.find_local_ip = network_fakes.get_local_ips(
+ local_ips=self._local_ips)
+
+ # Get the command object to test
+ self.cmd = local_ip.DeleteLocalIP(self.app, self.namespace)
+
+ def test_local_ip_delete(self):
+ arglist = [
+ self._local_ips[0].name,
+ ]
+ verifylist = [
+ ('local_ip', [self._local_ips[0].name]),
+ ]
+
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ result = self.cmd.take_action(parsed_args)
+ self.network.find_local_ip.assert_called_once_with(
+ self._local_ips[0].name, ignore_missing=False)
+ self.network.delete_local_ip.assert_called_once_with(
+ self._local_ips[0])
+ self.assertIsNone(result)
+
+ def test_multi_local_ips_delete(self):
+ arglist = []
+
+ for a in self._local_ips:
+ arglist.append(a.name)
+ verifylist = [
+ ('local_ip', arglist),
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ result = self.cmd.take_action(parsed_args)
+
+ calls = []
+ for a in self._local_ips:
+ calls.append(call(a))
+ self.network.delete_local_ip.assert_has_calls(calls)
+ self.assertIsNone(result)
+
+ def test_multi_local_ips_delete_with_exception(self):
+ arglist = [
+ self._local_ips[0].name,
+ 'unexist_local_ip',
+ ]
+ verifylist = [
+ ('local_ip',
+ [self._local_ips[0].name, 'unexist_local_ip']),
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ find_mock_result = [self._local_ips[0], exceptions.CommandError]
+ self.network.find_local_ip = (
+ mock.Mock(side_effect=find_mock_result)
+ )
+
+ try:
+ self.cmd.take_action(parsed_args)
+ self.fail('CommandError should be raised.')
+ except exceptions.CommandError as e:
+ self.assertEqual('1 of 2 local IPs failed to delete.', str(e))
+
+ self.network.find_local_ip.assert_any_call(
+ self._local_ips[0].name, ignore_missing=False)
+ self.network.find_local_ip.assert_any_call(
+ 'unexist_local_ip', ignore_missing=False)
+ self.network.delete_local_ip.assert_called_once_with(
+ self._local_ips[0]
+ )
+
+
+class TestListLocalIP(TestLocalIP):
+ # The local ip to list up.
+ local_ips = (
+ network_fakes.create_local_ips(count=3))
+ fake_network = network_fakes.FakeNetwork.create_one_network(
+ {'id': 'fake_network_id'}
+ )
+
+ columns = (
+ 'ID',
+ 'Name',
+ 'Description',
+ 'Project',
+ 'Local Port ID',
+ 'Network',
+ 'Local IP address',
+ 'IP mode',
+ )
+ data = []
+ for lip in local_ips:
+ data.append((
+ lip.id,
+ lip.name,
+ lip.description,
+ lip.project_id,
+ lip.local_port_id,
+ lip.network_id,
+ lip.local_ip_address,
+ lip.ip_mode,
+ ))
+
+ def setUp(self):
+ super().setUp()
+ self.network.local_ips = mock.Mock(
+ return_value=self.local_ips)
+ self.network.find_network = mock.Mock(
+ return_value=self.fake_network
+ )
+
+ # Get the command object to test
+ self.cmd = local_ip.ListLocalIP(self.app, self.namespace)
+
+ def test_local_ip_list(self):
+ arglist = []
+ verifylist = []
+
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+ columns, data = self.cmd.take_action(parsed_args)
+
+ self.network.local_ips.assert_called_once_with(**{})
+ self.assertEqual(self.columns, columns)
+ self.assertItemsEqual(self.data, list(data))
+
+ def test_local_ip_list_name(self):
+ arglist = [
+ '--name', self.local_ips[0].name,
+ ]
+ verifylist = [
+ ('name', self.local_ips[0].name),
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+ columns, data = self.cmd.take_action(parsed_args)
+
+ self.network.local_ips.assert_called_once_with(
+ **{'name': self.local_ips[0].name})
+ self.assertEqual(self.columns, columns)
+ self.assertItemsEqual(self.data, list(data))
+
+ def test_local_ip_list_project(self):
+ project = identity_fakes_v3.FakeProject.create_one_project()
+ self.projects_mock.get.return_value = project
+ arglist = [
+ '--project', project.id,
+ ]
+ verifylist = [
+ ('project', project.id),
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+ columns, data = self.cmd.take_action(parsed_args)
+
+ self.network.local_ips.assert_called_once_with(
+ **{'project_id': project.id})
+ self.assertEqual(self.columns, columns)
+ self.assertItemsEqual(self.data, list(data))
+
+ def test_local_ip_project_domain(self):
+ project = identity_fakes_v3.FakeProject.create_one_project()
+ self.projects_mock.get.return_value = project
+ arglist = [
+ '--project', project.id,
+ '--project-domain', project.domain_id,
+ ]
+ verifylist = [
+ ('project', project.id),
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ columns, data = self.cmd.take_action(parsed_args)
+ filters = {'project_id': project.id}
+
+ self.network.local_ips.assert_called_once_with(**filters)
+ self.assertEqual(self.columns, columns)
+ self.assertItemsEqual(self.data, list(data))
+
+ def test_local_ip_list_network(self):
+ arglist = [
+ '--network', 'fake_network_id',
+ ]
+ verifylist = [
+ ('network', 'fake_network_id'),
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ columns, data = self.cmd.take_action(parsed_args)
+
+ self.network.local_ips.assert_called_once_with(**{
+ 'network_id': 'fake_network_id',
+ })
+
+ self.assertEqual(self.columns, columns)
+ self.assertEqual(self.data, list(data))
+
+ def test_local_ip_list_local_ip_address(self):
+ arglist = [
+ '--local-ip-address', self.local_ips[0].local_ip_address,
+ ]
+ verifylist = [
+ ('local_ip_address', self.local_ips[0].local_ip_address),
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ columns, data = self.cmd.take_action(parsed_args)
+
+ self.network.local_ips.assert_called_once_with(**{
+ 'local_ip_address': self.local_ips[0].local_ip_address,
+ })
+ self.assertEqual(self.columns, columns)
+ self.assertEqual(self.data, list(data))
+
+ def test_local_ip_list_ip_mode(self):
+ arglist = [
+ '--ip-mode', self.local_ips[0].ip_mode,
+ ]
+ verifylist = [
+ ('ip_mode', self.local_ips[0].ip_mode),
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ columns, data = self.cmd.take_action(parsed_args)
+
+ self.network.local_ips.assert_called_once_with(**{
+ 'ip_mode': self.local_ips[0].ip_mode,
+ })
+ self.assertEqual(self.columns, columns)
+ self.assertEqual(self.data, list(data))
+
+
+class TestSetLocalIP(TestLocalIP):
+ # The local ip to set.
+ _local_ip = network_fakes.create_one_local_ip()
+
+ def setUp(self):
+ super().setUp()
+ self.network.update_local_ip = mock.Mock(return_value=None)
+ self.network.find_local_ip = mock.Mock(
+ return_value=self._local_ip)
+
+ # Get the command object to test
+ self.cmd = local_ip.SetLocalIP(self.app, self.namespace)
+
+ def test_set_nothing(self):
+ arglist = [self._local_ip.name, ]
+ verifylist = [
+ ('local_ip', self._local_ip.name),
+ ]
+
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+ result = self.cmd.take_action(parsed_args)
+
+ self.network.update_local_ip.assert_not_called()
+ self.assertIsNone(result)
+
+ def test_set_name_and_description(self):
+ arglist = [
+ '--name', 'new_local_ip_name',
+ '--description', 'new_local_ip_description',
+ self._local_ip.name,
+ ]
+ verifylist = [
+ ('name', 'new_local_ip_name'),
+ ('description', 'new_local_ip_description'),
+ ('local_ip', self._local_ip.name),
+ ]
+
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+ result = self.cmd.take_action(parsed_args)
+ attrs = {
+ 'name': "new_local_ip_name",
+ 'description': 'new_local_ip_description',
+ }
+ self.network.update_local_ip.assert_called_with(
+ self._local_ip, **attrs)
+ self.assertIsNone(result)
+
+
+class TestShowLocalIP(TestLocalIP):
+ # The local ip to show.
+ _local_ip = network_fakes.create_one_local_ip()
+ columns = (
+ 'created_at',
+ 'description',
+ 'id',
+ 'name',
+ 'project_id',
+ 'local_port_id',
+ 'network_id',
+ 'local_ip_address',
+ 'ip_mode',
+ 'revision_number',
+ 'updated_at',
+ )
+ data = (
+ _local_ip.created_at,
+ _local_ip.description,
+ _local_ip.id,
+ _local_ip.name,
+ _local_ip.project_id,
+ _local_ip.local_port_id,
+ _local_ip.network_id,
+ _local_ip.local_ip_address,
+ _local_ip.ip_mode,
+ _local_ip.revision_number,
+ _local_ip.updated_at,
+ )
+
+ def setUp(self):
+ super().setUp()
+ self.network.find_local_ip = mock.Mock(
+ return_value=self._local_ip)
+
+ # Get the command object to test
+ self.cmd = local_ip.ShowLocalIP(self.app, self.namespace)
+
+ def test_show_no_options(self):
+ arglist = []
+ verifylist = []
+
+ # Missing required args should bail here
+ self.assertRaises(tests_utils.ParserException, self.check_parser,
+ self.cmd, arglist, verifylist)
+
+ def test_show_all_options(self):
+ arglist = [
+ self._local_ip.name,
+ ]
+ verifylist = [
+ ('local_ip', self._local_ip.name),
+ ]
+
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+ columns, data = self.cmd.take_action(parsed_args)
+
+ self.network.find_local_ip.assert_called_once_with(
+ self._local_ip.name, ignore_missing=False)
+ self.assertEqual(set(self.columns), set(columns))
+ self.assertItemsEqual(self.data, list(data))
diff --git a/openstackclient/tests/unit/network/v2/test_local_ip_association.py b/openstackclient/tests/unit/network/v2/test_local_ip_association.py
new file mode 100644
index 00000000..97759302
--- /dev/null
+++ b/openstackclient/tests/unit/network/v2/test_local_ip_association.py
@@ -0,0 +1,328 @@
+# Copyright 2021 Huawei, Inc. 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.
+#
+
+from unittest import mock
+from unittest.mock import call
+
+from osc_lib import exceptions
+
+from openstackclient.network.v2 import local_ip_association
+from openstackclient.tests.unit.identity.v2_0 import fakes as identity_fakes_v2
+from openstackclient.tests.unit.network.v2 import fakes as network_fakes
+
+
+class TestLocalIPAssociation(network_fakes.TestNetworkV2):
+
+ def setUp(self):
+ super().setUp()
+ self.network = self.app.client_manager.network
+ self.local_ip = network_fakes.create_one_local_ip()
+ self.fixed_port = network_fakes.FakePort.create_one_port()
+ self.project = identity_fakes_v2.FakeProject.create_one_project()
+ self.network.find_port = mock.Mock(return_value=self.fixed_port)
+
+
+class TestCreateLocalIPAssociation(TestLocalIPAssociation):
+
+ def setUp(self):
+ super().setUp()
+ self.new_local_ip_association = (
+ network_fakes.create_one_local_ip_association(
+ attrs={
+ 'fixed_port_id': self.fixed_port.id,
+ 'local_ip_id': self.local_ip.id,
+ }
+ )
+ )
+ self.network.create_local_ip_association = mock.Mock(
+ return_value=self.new_local_ip_association)
+
+ self.network.find_local_ip = mock.Mock(
+ return_value=self.local_ip
+ )
+
+ # Get the command object to test
+ self.cmd = local_ip_association.CreateLocalIPAssociation(
+ self.app, self.namespace)
+
+ self.columns = (
+ 'local_ip_address',
+ 'fixed_port_id',
+ 'fixed_ip',
+ 'host',
+ )
+
+ self.data = (
+ self.new_local_ip_association.local_ip_address,
+ self.new_local_ip_association.fixed_port_id,
+ self.new_local_ip_association.fixed_ip,
+ self.new_local_ip_association.host,
+ )
+
+ def test_create_no_options(self):
+ arglist = [
+ self.new_local_ip_association.local_ip_id,
+ self.new_local_ip_association.fixed_port_id,
+ ]
+ verifylist = [
+ ('local_ip', self.new_local_ip_association.local_ip_id),
+ ('fixed_port', self.new_local_ip_association.fixed_port_id),
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+ columns, data = self.cmd.take_action(parsed_args)
+
+ self.network.create_local_ip_association.\
+ assert_called_once_with(
+ self.new_local_ip_association.local_ip_id,
+ **{
+ 'fixed_port_id':
+ self.new_local_ip_association.fixed_port_id,
+ })
+ self.assertEqual(set(self.columns), set(columns))
+ self.assertEqual(set(self.data), set(data))
+
+ def test_create_all_options(self):
+ arglist = [
+ self.new_local_ip_association.local_ip_id,
+ self.new_local_ip_association.fixed_port_id,
+ '--fixed-ip', self.new_local_ip_association.fixed_ip,
+ ]
+ verifylist = [
+ ('local_ip', self.new_local_ip_association.local_ip_id),
+ ('fixed_port', self.new_local_ip_association.fixed_port_id),
+ ('fixed_ip', self.new_local_ip_association.fixed_ip),
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+ columns, data = self.cmd.take_action(parsed_args)
+
+ self.network.create_local_ip_association.\
+ assert_called_once_with(
+ self.new_local_ip_association.local_ip_id,
+ **{
+ 'fixed_port_id':
+ self.new_local_ip_association.fixed_port_id,
+ 'fixed_ip':
+ self.new_local_ip_association.fixed_ip,
+ })
+ self.assertEqual(set(self.columns), set(columns))
+ self.assertEqual(set(self.data), set(data))
+
+
+class TestDeleteLocalIPAssociation(TestLocalIPAssociation):
+
+ def setUp(self):
+ super().setUp()
+ self._local_ip_association = (
+ network_fakes.create_local_ip_associations(
+ count=2, attrs={
+ 'local_ip_id': self.local_ip.id,
+ }
+ )
+ )
+ self.network.delete_local_ip_association = mock.Mock(
+ return_value=None
+ )
+
+ self.network.find_local_ip = mock.Mock(
+ return_value=self.local_ip
+ )
+ # Get the command object to test
+ self.cmd = local_ip_association.DeleteLocalIPAssociation(
+ self.app, self.namespace)
+
+ def test_local_ip_association_delete(self):
+ arglist = [
+ self.local_ip.id,
+ self._local_ip_association[0].fixed_port_id,
+ ]
+ verifylist = [
+ ('local_ip', self.local_ip.id),
+ ('fixed_port_id', [self._local_ip_association[0].fixed_port_id]),
+ ]
+
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ result = self.cmd.take_action(parsed_args)
+
+ self.network.delete_local_ip_association.\
+ assert_called_once_with(
+ self.local_ip.id,
+ self._local_ip_association[0].fixed_port_id,
+ ignore_missing=False
+ )
+
+ self.assertIsNone(result)
+
+ def test_multi_local_ip_associations_delete(self):
+ arglist = []
+ fixed_port_id = []
+
+ arglist.append(str(self.local_ip))
+
+ for a in self._local_ip_association:
+ arglist.append(a.fixed_port_id)
+ fixed_port_id.append(a.fixed_port_id)
+
+ verifylist = [
+ ('local_ip', str(self.local_ip)),
+ ('fixed_port_id', fixed_port_id),
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ result = self.cmd.take_action(parsed_args)
+
+ calls = []
+ for a in self._local_ip_association:
+ calls.append(call(a.local_ip_id, a.fixed_port_id,
+ ignore_missing=False))
+
+ self.network.delete_local_ip_association.assert_has_calls(calls)
+ self.assertIsNone(result)
+
+ def test_multi_local_ip_association_delete_with_exception(self):
+ arglist = [
+ self.local_ip.id,
+ self._local_ip_association[0].fixed_port_id,
+ 'unexist_fixed_port_id',
+ ]
+ verifylist = [
+ ('local_ip', self.local_ip.id),
+ ('fixed_port_id',
+ [self._local_ip_association[0].fixed_port_id,
+ 'unexist_fixed_port_id']),
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ delete_mock_result = [None, exceptions.CommandError]
+
+ self.network.delete_local_ip_association = (
+ mock.MagicMock(side_effect=delete_mock_result)
+ )
+
+ try:
+ self.cmd.take_action(parsed_args)
+ self.fail('CommandError should be raised.')
+ except exceptions.CommandError as e:
+ self.assertEqual(
+ '1 of 2 Local IP Associations failed to delete.',
+ str(e)
+ )
+
+ self.network.delete_local_ip_association.\
+ assert_any_call(
+ self.local_ip.id,
+ 'unexist_fixed_port_id',
+ ignore_missing=False
+ )
+ self.network.delete_local_ip_association.\
+ assert_any_call(
+ self.local_ip.id,
+ self._local_ip_association[0].fixed_port_id,
+ ignore_missing=False
+ )
+
+
+class TestListLocalIPAssociation(TestLocalIPAssociation):
+
+ columns = (
+ 'Local IP ID',
+ 'Local IP Address',
+ 'Fixed port ID',
+ 'Fixed IP',
+ 'Host'
+ )
+
+ def setUp(self):
+ super().setUp()
+ self.local_ip_associations = (
+ network_fakes.create_local_ip_associations(
+ count=3, attrs={
+ 'local_ip_id': self.local_ip.id,
+ 'fixed_port_id': self.fixed_port.id,
+ }
+ )
+ )
+ self.data = []
+ for lip_assoc in self.local_ip_associations:
+ self.data.append((
+ lip_assoc.local_ip_id,
+ lip_assoc.local_ip_address,
+ lip_assoc.fixed_port_id,
+ lip_assoc.fixed_ip,
+ lip_assoc.host,
+ ))
+ self.network.local_ip_associations = mock.Mock(
+ return_value=self.local_ip_associations
+ )
+ self.network.find_local_ip = mock.Mock(
+ return_value=self.local_ip
+ )
+ self.network.find_port = mock.Mock(
+ return_value=self.fixed_port
+ )
+ # Get the command object to test
+ self.cmd = local_ip_association.ListLocalIPAssociation(
+ self.app,
+ self.namespace
+ )
+
+ def test_local_ip_association_list(self):
+ arglist = [
+ self.local_ip.id
+ ]
+ verifylist = [
+ ('local_ip', self.local_ip.id)
+ ]
+
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+ columns, data = self.cmd.take_action(parsed_args)
+
+ self.network.local_ip_associations.assert_called_once_with(
+ self.local_ip,
+ **{}
+ )
+ self.assertEqual(set(self.columns), set(columns))
+ self.assertEqual(set(self.data), set(list(data)))
+
+ def test_local_ip_association_list_all_options(self):
+ arglist = [
+ '--fixed-port', self.local_ip_associations[0].fixed_port_id,
+ '--fixed-ip', self.local_ip_associations[0].fixed_ip,
+ '--host', self.local_ip_associations[0].host,
+ self.local_ip_associations[0].local_ip_id
+ ]
+
+ verifylist = [
+ ('fixed_port', self.local_ip_associations[0].fixed_port_id),
+ ('fixed_ip', self.local_ip_associations[0].fixed_ip),
+ ('host', self.local_ip_associations[0].host),
+ ('local_ip', self.local_ip_associations[0].local_ip_id),
+ ]
+
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+ columns, data = self.cmd.take_action(parsed_args)
+
+ attrs = {
+ 'fixed_port_id': self.local_ip_associations[0].fixed_port_id,
+ 'fixed_ip': self.local_ip_associations[0].fixed_ip,
+ 'host': self.local_ip_associations[0].host,
+ }
+
+ self.network.local_ip_associations.assert_called_once_with(
+ self.local_ip,
+ **attrs
+ )
+ self.assertEqual(set(self.columns), set(columns))
+ self.assertEqual(set(self.data), set(list(data)))
diff --git a/openstackclient/tests/unit/network/v2/test_network.py b/openstackclient/tests/unit/network/v2/test_network.py
index 127d82b0..2b04edf5 100644
--- a/openstackclient/tests/unit/network/v2/test_network.py
+++ b/openstackclient/tests/unit/network/v2/test_network.py
@@ -48,7 +48,7 @@ class TestCreateNetworkIdentityV3(TestNetwork):
# The new network created.
_network = network_fakes.FakeNetwork.create_one_network(
attrs={
- 'tenant_id': project.id,
+ 'project_id': project.id,
'availability_zone_hints': ["nova"],
}
)
@@ -197,8 +197,6 @@ class TestCreateNetworkIdentityV3(TestNetwork):
'shared': True,
'description': self._network.description,
'mtu': self._network.mtu,
- # TODO(dtroyer): Remove tenant_id when we clean up the SDK refactor
- 'tenant_id': self.project.id,
'project_id': self.project.id,
'is_default': True,
'router:external': True,
@@ -284,7 +282,7 @@ class TestCreateNetworkIdentityV2(TestNetwork):
project = identity_fakes_v2.FakeProject.create_one_project()
# The new network created.
_network = network_fakes.FakeNetwork.create_one_network(
- attrs={'tenant_id': project.id}
+ attrs={'project_id': project.id}
)
columns = (
@@ -379,8 +377,6 @@ class TestCreateNetworkIdentityV2(TestNetwork):
self.network.create_network.assert_called_once_with(**{
'admin_state_up': True,
'name': self._network.name,
- # TODO(dtroyer): Remove tenant_id when we clean up the SDK refactor
- 'tenant_id': self.project.id,
'project_id': self.project.id,
})
self.assertFalse(self.network.set_tags.called)
@@ -704,7 +700,7 @@ class TestListNetwork(TestNetwork):
columns, data = self.cmd.take_action(parsed_args)
self.network.networks.assert_called_once_with(
- **{'tenant_id': project.id, 'project_id': project.id}
+ **{'project_id': project.id}
)
self.assertEqual(self.columns, columns)
@@ -723,7 +719,7 @@ class TestListNetwork(TestNetwork):
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
columns, data = self.cmd.take_action(parsed_args)
- filters = {'tenant_id': project.id, 'project_id': project.id}
+ filters = {'project_id': project.id}
self.network.networks.assert_called_once_with(**filters)
self.assertEqual(self.columns, columns)
diff --git a/openstackclient/tests/unit/network/v2/test_network_auto_allocated_topology.py b/openstackclient/tests/unit/network/v2/test_network_auto_allocated_topology.py
index e9687a70..d77d6894 100644
--- a/openstackclient/tests/unit/network/v2/test_network_auto_allocated_topology.py
+++ b/openstackclient/tests/unit/network/v2/test_network_auto_allocated_topology.py
@@ -33,7 +33,7 @@ class TestCreateAutoAllocatedTopology(TestAutoAllocatedTopology):
topology = network_fakes.FakeAutoAllocatedTopology.create_one_topology(
attrs={'id': network_object.id,
- 'tenant_id': project.id}
+ 'project_id': project.id}
)
columns = (
@@ -131,7 +131,7 @@ class TestValidateAutoAllocatedTopology(TestAutoAllocatedTopology):
topology = network_fakes.FakeAutoAllocatedTopology.create_one_topology(
attrs={'id': network_object.id,
- 'tenant_id': project.id}
+ 'project_id': project.id}
)
columns = (
@@ -208,7 +208,7 @@ class TestDeleteAutoAllocatedTopology(TestAutoAllocatedTopology):
topology = network_fakes.FakeAutoAllocatedTopology.create_one_topology(
attrs={'id': network_object.id,
- 'tenant_id': project.id}
+ 'project_id': project.id}
)
def setUp(self):
diff --git a/openstackclient/tests/unit/network/v2/test_network_flavor.py b/openstackclient/tests/unit/network/v2/test_network_flavor.py
index 010f53d3..20c5b9d6 100644
--- a/openstackclient/tests/unit/network/v2/test_network_flavor.py
+++ b/openstackclient/tests/unit/network/v2/test_network_flavor.py
@@ -166,7 +166,7 @@ class TestCreateNetworkFlavor(TestNetworkFlavor):
self.network.create_flavor.assert_called_once_with(**{
'description': self.new_network_flavor.description,
'enabled': True,
- 'tenant_id': self.project.id,
+ 'project_id': self.project.id,
'service_type': self.new_network_flavor.service_type,
'name': self.new_network_flavor.name,
})
diff --git a/openstackclient/tests/unit/network/v2/test_network_flavor_profile.py b/openstackclient/tests/unit/network/v2/test_network_flavor_profile.py
index fcf24da9..1cbe30ba 100644
--- a/openstackclient/tests/unit/network/v2/test_network_flavor_profile.py
+++ b/openstackclient/tests/unit/network/v2/test_network_flavor_profile.py
@@ -89,7 +89,7 @@ class TestCreateFlavorProfile(TestFlavorProfile):
self.network.create_service_profile.assert_called_once_with(
**{'description': self.new_flavor_profile.description,
- 'tenant_id': self.project.id,
+ 'project_id': self.project.id,
'enabled': self.new_flavor_profile.enabled,
'driver': self.new_flavor_profile.driver,
'metainfo': self.new_flavor_profile.metainfo}
@@ -119,7 +119,7 @@ class TestCreateFlavorProfile(TestFlavorProfile):
self.network.create_service_profile.assert_called_once_with(
**{'description': self.new_flavor_profile.description,
- 'tenant_id': self.project.id,
+ 'project_id': self.project.id,
'enabled': self.new_flavor_profile.enabled,
'metainfo': self.new_flavor_profile.metainfo}
)
@@ -148,7 +148,7 @@ class TestCreateFlavorProfile(TestFlavorProfile):
self.network.create_service_profile.assert_called_once_with(
**{'description': self.new_flavor_profile.description,
- 'tenant_id': self.project.id,
+ 'project_id': self.project.id,
'enabled': self.new_flavor_profile.enabled,
'driver': self.new_flavor_profile.driver,
}
diff --git a/openstackclient/tests/unit/network/v2/test_network_meter.py b/openstackclient/tests/unit/network/v2/test_network_meter.py
index 4fadcfe1..5cedf0f4 100644
--- a/openstackclient/tests/unit/network/v2/test_network_meter.py
+++ b/openstackclient/tests/unit/network/v2/test_network_meter.py
@@ -112,7 +112,7 @@ class TestCreateMeter(TestMeter):
self.network.create_metering_label.assert_called_once_with(
**{'description': self.new_meter.description,
'name': self.new_meter.name,
- 'tenant_id': self.project.id,
+ 'project_id': self.project.id,
'shared': True, }
)
self.assertEqual(self.columns, columns)
diff --git a/openstackclient/tests/unit/network/v2/test_network_qos_policy.py b/openstackclient/tests/unit/network/v2/test_network_qos_policy.py
index d6a78410..af4cb3fb 100644
--- a/openstackclient/tests/unit/network/v2/test_network_qos_policy.py
+++ b/openstackclient/tests/unit/network/v2/test_network_qos_policy.py
@@ -42,7 +42,7 @@ class TestCreateNetworkQosPolicy(TestQosPolicy):
new_qos_policy = (
network_fakes.FakeNetworkQosPolicy.create_one_qos_policy(
attrs={
- 'tenant_id': project.id,
+ 'project_id': project.id,
}
))
columns = (
@@ -123,7 +123,7 @@ class TestCreateNetworkQosPolicy(TestQosPolicy):
self.network.create_qos_policy.assert_called_once_with(**{
'shared': True,
- 'tenant_id': self.project.id,
+ 'project_id': self.project.id,
'name': self.new_qos_policy.name,
'description': 'QoS policy description',
'is_default': True,
@@ -325,7 +325,7 @@ class TestListNetworkQosPolicy(TestQosPolicy):
columns, data = self.cmd.take_action(parsed_args)
self.network.qos_policies.assert_called_once_with(
- **{'tenant_id': project.id}
+ **{'project_id': project.id}
)
self.assertEqual(self.columns, columns)
diff --git a/openstackclient/tests/unit/network/v2/test_network_rbac.py b/openstackclient/tests/unit/network/v2/test_network_rbac.py
index 08be64c5..e20efc8b 100644
--- a/openstackclient/tests/unit/network/v2/test_network_rbac.py
+++ b/openstackclient/tests/unit/network/v2/test_network_rbac.py
@@ -45,7 +45,7 @@ class TestCreateNetworkRBAC(TestNetworkRBAC):
ag_object = network_fakes.FakeAddressGroup.create_one_address_group()
project = identity_fakes_v3.FakeProject.create_one_project()
rbac_policy = network_fakes.FakeNetworkRBAC.create_one_network_rbac(
- attrs={'tenant_id': project.id,
+ attrs={'project_id': project.id,
'target_tenant': project.id,
'object_id': network_object.id}
)
@@ -64,7 +64,7 @@ class TestCreateNetworkRBAC(TestNetworkRBAC):
rbac_policy.id,
rbac_policy.object_id,
rbac_policy.object_type,
- rbac_policy.tenant_id,
+ rbac_policy.project_id,
rbac_policy.target_tenant,
]
@@ -206,7 +206,7 @@ class TestCreateNetworkRBAC(TestNetworkRBAC):
'--type', self.rbac_policy.object_type,
'--action', self.rbac_policy.action,
'--target-project', self.rbac_policy.target_tenant,
- '--project', self.rbac_policy.tenant_id,
+ '--project', self.rbac_policy.project_id,
'--project-domain', self.project.domain_id,
'--target-project-domain', self.project.domain_id,
self.rbac_policy.object_id,
@@ -215,7 +215,7 @@ class TestCreateNetworkRBAC(TestNetworkRBAC):
('type', self.rbac_policy.object_type),
('action', self.rbac_policy.action),
('target_project', self.rbac_policy.target_tenant),
- ('project', self.rbac_policy.tenant_id),
+ ('project', self.rbac_policy.project_id),
('project_domain', self.project.domain_id),
('target_project_domain', self.project.domain_id),
('rbac_object', self.rbac_policy.object_id),
@@ -230,7 +230,7 @@ class TestCreateNetworkRBAC(TestNetworkRBAC):
'object_type': self.rbac_policy.object_type,
'action': self.rbac_policy.action,
'target_tenant': self.rbac_policy.target_tenant,
- 'tenant_id': self.rbac_policy.tenant_id,
+ 'project_id': self.rbac_policy.project_id,
})
self.assertEqual(self.columns, columns)
self.assertEqual(self.data, list(data))
@@ -276,7 +276,7 @@ class TestCreateNetworkRBAC(TestNetworkRBAC):
self.rbac_policy.id,
obj_fake.id,
obj_type,
- self.rbac_policy.tenant_id,
+ self.rbac_policy.project_id,
self.rbac_policy.target_tenant,
]
self.assertEqual(self.columns, columns)
@@ -541,7 +541,7 @@ class TestShowNetworkRBAC(TestNetworkRBAC):
rbac_policy.id,
rbac_policy.object_id,
rbac_policy.object_type,
- rbac_policy.tenant_id,
+ rbac_policy.project_id,
rbac_policy.target_tenant,
]
diff --git a/openstackclient/tests/unit/network/v2/test_port.py b/openstackclient/tests/unit/network/v2/test_port.py
index 5f2a1283..3c18f362 100644
--- a/openstackclient/tests/unit/network/v2/test_port.py
+++ b/openstackclient/tests/unit/network/v2/test_port.py
@@ -1217,7 +1217,6 @@ class TestListPort(TestPort):
columns, data = self.cmd.take_action(parsed_args)
filters = {
- 'tenant_id': project.id,
'project_id': project.id,
'fields': LIST_FIELDS_TO_RETRIEVE,
}
@@ -1241,7 +1240,6 @@ class TestListPort(TestPort):
columns, data = self.cmd.take_action(parsed_args)
filters = {
- 'tenant_id': project.id,
'project_id': project.id,
'fields': LIST_FIELDS_TO_RETRIEVE,
}
@@ -1296,6 +1294,26 @@ class TestListPort(TestPort):
self.assertEqual(self.columns, columns)
self.assertCountEqual(self.data, list(data))
+ def test_port_list_security_group(self):
+ arglist = [
+ '--security-group', 'sg-id1',
+ '--security-group', 'sg-id2',
+ ]
+ verifylist = [
+ ('security_groups', ['sg-id1', 'sg-id2']),
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ columns, data = self.cmd.take_action(parsed_args)
+ filters = {
+ 'security_groups': ['sg-id1', 'sg-id2'],
+ 'fields': LIST_FIELDS_TO_RETRIEVE,
+ }
+
+ self.network.ports.assert_called_once_with(**filters)
+ self.assertEqual(self.columns, columns)
+ self.assertCountEqual(self.data, list(data))
+
class TestSetPort(TestPort):
@@ -1923,6 +1941,7 @@ class TestUnsetPort(TestPort):
'subnet=042eb10a-3a18-4658-ab-cf47c8d03152,ip-address=1.0.0.0',
'--binding-profile', 'Superman',
'--qos-policy',
+ '--host',
self._testport.name,
]
verifylist = [
@@ -1931,6 +1950,7 @@ class TestUnsetPort(TestPort):
'ip-address': '1.0.0.0'}]),
('binding_profile', ['Superman']),
('qos_policy', True),
+ ('host', True)
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
@@ -1941,7 +1961,8 @@ class TestUnsetPort(TestPort):
'subnet_id': '042eb10a-3a18-4658-ab-cf47c8d03152',
'ip_address': '0.0.0.1'}],
'binding:profile': {'batman': 'Joker'},
- 'qos_policy_id': None
+ 'qos_policy_id': None,
+ 'binding:host_id': None
}
self.network.update_port.assert_called_once_with(
self._testport, **attrs)
diff --git a/openstackclient/tests/unit/network/v2/test_router.py b/openstackclient/tests/unit/network/v2/test_router.py
index 03246748..4d035077 100644
--- a/openstackclient/tests/unit/network/v2/test_router.py
+++ b/openstackclient/tests/unit/network/v2/test_router.py
@@ -141,7 +141,7 @@ class TestCreateRouter(TestRouter):
new_router.ha,
new_router.id,
new_router.name,
- new_router.tenant_id,
+ new_router.project_id,
router.RoutesColumn(new_router.routes),
new_router.status,
format_columns.ListColumn(new_router.tags),
@@ -186,6 +186,43 @@ class TestCreateRouter(TestRouter):
self.assertEqual(self.columns, columns)
self.assertCountEqual(self.data, data)
+ def test_create_with_gateway(self):
+ _network = network_fakes.FakeNetwork.create_one_network()
+ _subnet = network_fakes.FakeSubnet.create_one_subnet()
+ self.network.find_network = mock.Mock(return_value=_network)
+ self.network.find_subnet = mock.Mock(return_value=_subnet)
+ arglist = [
+ self.new_router.name,
+ '--external-gateway', _network.name,
+ '--enable-snat',
+ '--fixed-ip', 'ip-address=2001:db8::1'
+ ]
+ verifylist = [
+ ('name', self.new_router.name),
+ ('enable', True),
+ ('distributed', False),
+ ('ha', False),
+ ('external_gateway', _network.name),
+ ('enable_snat', True),
+ ('fixed_ip', [{'ip-address': '2001:db8::1'}]),
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ columns, data = (self.cmd.take_action(parsed_args))
+
+ self.network.create_router.assert_called_once_with(**{
+ 'admin_state_up': True,
+ 'name': self.new_router.name,
+ 'external_gateway_info': {
+ 'network_id': _network.id,
+ 'enable_snat': True,
+ 'external_fixed_ips': [{'ip_address': '2001:db8::1'}],
+ },
+ })
+ self.assertFalse(self.network.set_tags.called)
+ self.assertEqual(self.columns, columns)
+ self.assertCountEqual(self.data, data)
+
def _test_create_with_ha_options(self, option, ha):
arglist = [
option,
@@ -423,7 +460,7 @@ class TestListRouter(TestRouter):
r.name,
r.status,
router.AdminStateColumn(r.admin_state_up),
- r.tenant_id,
+ r.project_id,
r.distributed,
r.ha,
))
@@ -619,7 +656,7 @@ class TestListRouter(TestRouter):
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
columns, data = self.cmd.take_action(parsed_args)
- filters = {'tenant_id': project.id, 'project_id': project.id}
+ filters = {'project_id': project.id}
self.network.routers.assert_called_once_with(**filters)
self.assertEqual(self.columns, columns)
@@ -639,7 +676,7 @@ class TestListRouter(TestRouter):
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
columns, data = self.cmd.take_action(parsed_args)
- filters = {'tenant_id': project.id, 'project_id': project.id}
+ filters = {'project_id': project.id}
self.network.routers.assert_called_once_with(**filters)
self.assertEqual(self.columns, columns)
@@ -1364,7 +1401,7 @@ class TestShowRouter(TestRouter):
_router.id,
router.RouterInfoColumn(_router.interfaces_info),
_router.name,
- _router.tenant_id,
+ _router.project_id,
router.RoutesColumn(_router.routes),
_router.status,
format_columns.ListColumn(_router.tags),
diff --git a/openstackclient/tests/unit/network/v2/test_security_group_network.py b/openstackclient/tests/unit/network/v2/test_security_group_network.py
index 569c0cd5..95262bf1 100644
--- a/openstackclient/tests/unit/network/v2/test_security_group_network.py
+++ b/openstackclient/tests/unit/network/v2/test_security_group_network.py
@@ -121,7 +121,7 @@ class TestCreateSecurityGroupNetwork(TestSecurityGroupNetwork):
'description': self._security_group.description,
'stateful': self._security_group.stateful,
'name': self._security_group.name,
- 'tenant_id': self.project.id,
+ 'project_id': self.project.id,
})
self.assertEqual(self.columns, columns)
self.assertCountEqual(self.data, data)
@@ -324,7 +324,7 @@ class TestListSecurityGroupNetwork(TestSecurityGroupNetwork):
columns, data = self.cmd.take_action(parsed_args)
filters = {
- 'tenant_id': project.id, 'project_id': project.id,
+ 'project_id': project.id,
'fields': security_group.ListSecurityGroup.FIELDS_TO_RETRIEVE}
self.network.security_groups.assert_called_once_with(**filters)
@@ -346,7 +346,7 @@ class TestListSecurityGroupNetwork(TestSecurityGroupNetwork):
columns, data = self.cmd.take_action(parsed_args)
filters = {
- 'tenant_id': project.id, 'project_id': project.id,
+ 'project_id': project.id,
'fields': security_group.ListSecurityGroup.FIELDS_TO_RETRIEVE}
self.network.security_groups.assert_called_once_with(**filters)
diff --git a/openstackclient/tests/unit/network/v2/test_security_group_rule_network.py b/openstackclient/tests/unit/network/v2/test_security_group_rule_network.py
index bcdb0c26..4e765f3d 100644
--- a/openstackclient/tests/unit/network/v2/test_security_group_rule_network.py
+++ b/openstackclient/tests/unit/network/v2/test_security_group_rule_network.py
@@ -458,7 +458,7 @@ class TestCreateSecurityGroupRuleNetwork(TestSecurityGroupRuleNetwork):
'protocol': self._security_group_rule.protocol,
'remote_ip_prefix': self._security_group_rule.remote_ip_prefix,
'security_group_id': self._security_group.id,
- 'tenant_id': self.project.id,
+ 'project_id': self.project.id,
})
self.assertEqual(self.expected_columns, columns)
self.assertEqual(self.expected_data, data)
diff --git a/openstackclient/tests/unit/network/v2/test_subnet.py b/openstackclient/tests/unit/network/v2/test_subnet.py
index 5147b64d..dcac2ef8 100644
--- a/openstackclient/tests/unit/network/v2/test_subnet.py
+++ b/openstackclient/tests/unit/network/v2/test_subnet.py
@@ -44,7 +44,7 @@ class TestCreateSubnet(TestSubnet):
# An IPv4 subnet to be created with mostly default values
self._subnet = network_fakes.FakeSubnet.create_one_subnet(
attrs={
- 'tenant_id': self.project.id,
+ 'project_id': self.project.id,
}
)
@@ -55,7 +55,7 @@ class TestCreateSubnet(TestSubnet):
# An IPv4 subnet to be created using a specific subnet pool
self._subnet_from_pool = network_fakes.FakeSubnet.create_one_subnet(
attrs={
- 'tenant_id': self.project.id,
+ 'project_id': self.project.id,
'subnetpool_id': self._subnet_pool.id,
'dns_nameservers': ['8.8.8.8',
'8.8.4.4'],
@@ -71,7 +71,7 @@ class TestCreateSubnet(TestSubnet):
# An IPv6 subnet to be created with most options specified
self._subnet_ipv6 = network_fakes.FakeSubnet.create_one_subnet(
attrs={
- 'tenant_id': self.project.id,
+ 'project_id': self.project.id,
'cidr': 'fe80:0:0:a00a::/64',
'enable_dhcp': True,
'dns_nameservers': ['fe80:27ff:a00a:f00f::ffff',
@@ -661,7 +661,7 @@ class TestListSubnet(TestSubnet):
subnet.name,
subnet.network_id,
subnet.cidr,
- subnet.tenant_id,
+ subnet.project_id,
subnet.enable_dhcp,
format_columns.ListColumn(subnet.dns_nameservers),
subnet_v2.AllocationPoolsColumn(subnet.allocation_pools),
@@ -783,7 +783,7 @@ class TestListSubnet(TestSubnet):
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
columns, data = self.cmd.take_action(parsed_args)
- filters = {'tenant_id': project.id, 'project_id': project.id}
+ filters = {'project_id': project.id}
self.network.subnets.assert_called_once_with(**filters)
self.assertEqual(self.columns, columns)
@@ -821,7 +821,7 @@ class TestListSubnet(TestSubnet):
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
columns, data = self.cmd.take_action(parsed_args)
- filters = {'tenant_id': project.id, 'project_id': project.id}
+ filters = {'project_id': project.id}
self.network.subnets.assert_called_once_with(**filters)
self.assertEqual(self.columns, columns)
@@ -899,6 +899,48 @@ class TestListSubnet(TestSubnet):
self.assertEqual(self.columns, columns)
self.assertCountEqual(self.data, list(data))
+ def test_subnet_list_subnetpool_by_name(self):
+ subnet_pool = network_fakes.FakeSubnetPool.create_one_subnet_pool()
+ subnet = network_fakes.FakeSubnet.create_one_subnet(
+ {'subnetpool_id': subnet_pool.id})
+ self.network.find_network = mock.Mock(return_value=subnet)
+ self.network.find_subnet_pool = mock.Mock(return_value=subnet_pool)
+ arglist = [
+ '--subnet-pool', subnet_pool.name,
+ ]
+ verifylist = [
+ ('subnet_pool', subnet_pool.name),
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ columns, data = self.cmd.take_action(parsed_args)
+ filters = {'subnetpool_id': subnet_pool.id}
+
+ self.network.subnets.assert_called_once_with(**filters)
+ self.assertEqual(self.columns, columns)
+ self.assertItemsEqual(self.data, list(data))
+
+ def test_subnet_list_subnetpool_by_id(self):
+ subnet_pool = network_fakes.FakeSubnetPool.create_one_subnet_pool()
+ subnet = network_fakes.FakeSubnet.create_one_subnet(
+ {'subnetpool_id': subnet_pool.id})
+ self.network.find_network = mock.Mock(return_value=subnet)
+ self.network.find_subnet_pool = mock.Mock(return_value=subnet_pool)
+ arglist = [
+ '--subnet-pool', subnet_pool.id,
+ ]
+ verifylist = [
+ ('subnet_pool', subnet_pool.id),
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ columns, data = self.cmd.take_action(parsed_args)
+ filters = {'subnetpool_id': subnet_pool.id}
+
+ self.network.subnets.assert_called_once_with(**filters)
+ self.assertEqual(self.columns, columns)
+ self.assertItemsEqual(self.data, list(data))
+
def test_list_with_tag_options(self):
arglist = [
'--tags', 'red,blue',
@@ -1205,7 +1247,7 @@ class TestShowSubnet(TestSubnet):
_subnet.ipv6_ra_mode,
_subnet.name,
_subnet.network_id,
- _subnet.tenant_id,
+ _subnet.project_id,
_subnet.segment_id,
format_columns.ListColumn(_subnet.service_types),
_subnet.subnetpool_id,
diff --git a/openstackclient/tests/unit/network/v2/test_subnet_pool.py b/openstackclient/tests/unit/network/v2/test_subnet_pool.py
index 4d18dc99..55d2cc29 100644
--- a/openstackclient/tests/unit/network/v2/test_subnet_pool.py
+++ b/openstackclient/tests/unit/network/v2/test_subnet_pool.py
@@ -197,7 +197,7 @@ class TestCreateSubnetPool(TestSubnetPool):
self.network.create_subnet_pool.assert_called_once_with(**{
'prefixes': ['10.0.10.0/24'],
- 'tenant_id': self.project.id,
+ 'project_id': self.project.id,
'name': self._subnet_pool.name,
})
self.assertEqual(self.columns, columns)
@@ -569,7 +569,7 @@ class TestListSubnetPool(TestSubnetPool):
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
columns, data = self.cmd.take_action(parsed_args)
- filters = {'tenant_id': project.id, 'project_id': project.id}
+ filters = {'project_id': project.id}
self.network.subnet_pools.assert_called_once_with(**filters)
self.assertEqual(self.columns, columns)
@@ -589,7 +589,7 @@ class TestListSubnetPool(TestSubnetPool):
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
columns, data = self.cmd.take_action(parsed_args)
- filters = {'tenant_id': project.id, 'project_id': project.id}
+ filters = {'project_id': project.id}
self.network.subnet_pools.assert_called_once_with(**filters)
self.assertEqual(self.columns, columns)
@@ -970,7 +970,7 @@ class TestShowSubnetPool(TestSubnetPool):
_subnet_pool.min_prefixlen,
_subnet_pool.name,
format_columns.ListColumn(_subnet_pool.prefixes),
- _subnet_pool.tenant_id,
+ _subnet_pool.project_id,
_subnet_pool.shared,
format_columns.ListColumn(_subnet_pool.tags),
)
diff --git a/openstackclient/tests/unit/volume/v2/fakes.py b/openstackclient/tests/unit/volume/v2/fakes.py
index b5f66d4b..96e381d3 100644
--- a/openstackclient/tests/unit/volume/v2/fakes.py
+++ b/openstackclient/tests/unit/volume/v2/fakes.py
@@ -18,6 +18,7 @@ from unittest import mock
import uuid
from cinderclient import api_versions
+from openstack.block_storage.v3 import volume
from osc_lib.cli import format_columns
from openstackclient.tests.unit import fakes
@@ -46,7 +47,7 @@ class FakeTransfer(object):
def create_one_transfer(attrs=None):
"""Create a fake transfer.
- :param Dictionary attrs:
+ :param dict attrs:
A dictionary with all attributes of Transfer Request
:return:
A FakeResource object with volume_id, name, id.
@@ -75,7 +76,7 @@ class FakeTransfer(object):
def create_transfers(attrs=None, count=2):
"""Create multiple fake transfers.
- :param Dictionary attrs:
+ :param dict attrs:
A dictionary with all attributes of transfer
:param Integer count:
The number of transfers to be faked
@@ -116,7 +117,7 @@ class FakeTypeAccess(object):
def create_one_type_access(attrs=None):
"""Create a fake volume type access for project.
- :param Dictionary attrs:
+ :param dict attrs:
A dictionary with all attributes
:return:
A FakeResource object, with Volume_type_ID and Project_ID.
@@ -148,7 +149,7 @@ class FakeService(object):
def create_one_service(attrs=None):
"""Create a fake service.
- :param Dictionary attrs:
+ :param dict attrs:
A dictionary with all attributes of service
:return:
A FakeResource object with host, status, etc.
@@ -180,7 +181,7 @@ class FakeService(object):
def create_services(attrs=None, count=2):
"""Create multiple fake services.
- :param Dictionary attrs:
+ :param dict attrs:
A dictionary with all attributes of service
:param Integer count:
The number of services to be faked
@@ -201,7 +202,7 @@ class FakeCapability(object):
def create_one_capability(attrs=None):
"""Create a fake volume backend capability.
- :param Dictionary attrs:
+ :param dict attrs:
A dictionary with all attributes of the Capabilities.
:return:
A FakeResource object with capability name and attrs.
@@ -260,7 +261,7 @@ class FakePool(object):
def create_one_pool(attrs=None):
"""Create a fake pool.
- :param Dictionary attrs:
+ :param dict attrs:
A dictionary with all attributes of the pool
:return:
A FakeResource object with pool name and attrs.
@@ -362,7 +363,7 @@ class FakeVolume(object):
def create_one_volume(attrs=None):
"""Create a fake volume.
- :param Dictionary attrs:
+ :param dict attrs:
A dictionary with all attributes of volume
:return:
A FakeResource object with id, name, status, etc.
@@ -405,7 +406,7 @@ class FakeVolume(object):
def create_volumes(attrs=None, count=2):
"""Create multiple fake volumes.
- :param Dictionary attrs:
+ :param dict attrs:
A dictionary with all attributes of volume
:param Integer count:
The number of volumes to be faked
@@ -419,6 +420,61 @@ class FakeVolume(object):
return volumes
@staticmethod
+ def create_one_sdk_volume(attrs=None):
+ """Create a fake volume.
+
+ :param dict attrs:
+ A dictionary with all attributes of volume
+ :return:
+ A FakeResource object with id, name, status, etc.
+ """
+ attrs = attrs or {}
+
+ # Set default attribute
+ volume_info = {
+ 'id': 'volume-id' + uuid.uuid4().hex,
+ 'name': 'volume-name' + uuid.uuid4().hex,
+ 'description': 'description' + uuid.uuid4().hex,
+ 'status': random.choice(['available', 'in_use']),
+ 'size': random.randint(1, 20),
+ 'volume_type':
+ random.choice(['fake_lvmdriver-1', 'fake_lvmdriver-2']),
+ 'bootable':
+ random.choice(['true', 'false']),
+ 'metadata': {
+ 'key' + uuid.uuid4().hex: 'val' + uuid.uuid4().hex,
+ 'key' + uuid.uuid4().hex: 'val' + uuid.uuid4().hex,
+ 'key' + uuid.uuid4().hex: 'val' + uuid.uuid4().hex},
+ 'snapshot_id': random.randint(1, 5),
+ 'availability_zone': 'zone' + uuid.uuid4().hex,
+ 'attachments': [{
+ 'device': '/dev/' + uuid.uuid4().hex,
+ 'server_id': uuid.uuid4().hex,
+ }, ],
+ }
+
+ # Overwrite default attributes if there are some attributes set
+ volume_info.update(attrs)
+ return volume.Volume(**volume_info)
+
+ @staticmethod
+ def create_sdk_volumes(attrs=None, count=2):
+ """Create multiple fake volumes.
+
+ :param dict attrs:
+ A dictionary with all attributes of volume
+ :param Integer count:
+ The number of volumes to be faked
+ :return:
+ A list of FakeResource objects
+ """
+ volumes = []
+ for n in range(0, count):
+ volumes.append(FakeVolume.create_one_sdk_volume(attrs))
+
+ return volumes
+
+ @staticmethod
def get_volumes(volumes=None, count=2):
"""Get an iterable MagicMock object with a list of faked volumes.
@@ -484,7 +540,7 @@ class FakeAvailabilityZone(object):
def create_one_availability_zone(attrs=None):
"""Create a fake AZ.
- :param Dictionary attrs:
+ :param dict attrs:
A dictionary with all attributes
:return:
A FakeResource object with zoneName, zoneState, etc.
@@ -509,7 +565,7 @@ class FakeAvailabilityZone(object):
def create_availability_zones(attrs=None, count=2):
"""Create multiple fake AZs.
- :param Dictionary attrs:
+ :param dict attrs:
A dictionary with all attributes
:param int count:
The number of AZs to fake
@@ -532,7 +588,7 @@ class FakeBackup(object):
def create_one_backup(attrs=None):
"""Create a fake backup.
- :param Dictionary attrs:
+ :param dict attrs:
A dictionary with all attributes
:return:
A FakeResource object with id, name, volume_id, etc.
@@ -565,7 +621,7 @@ class FakeBackup(object):
def create_backups(attrs=None, count=2):
"""Create multiple fake backups.
- :param Dictionary attrs:
+ :param dict attrs:
A dictionary with all attributes
:param int count:
The number of backups to fake
@@ -636,7 +692,7 @@ class FakeConsistencyGroup(object):
def create_one_consistency_group(attrs=None):
"""Create a fake consistency group.
- :param Dictionary attrs:
+ :param dict attrs:
A dictionary with all attributes
:return:
A FakeResource object with id, name, description, etc.
@@ -666,7 +722,7 @@ class FakeConsistencyGroup(object):
def create_consistency_groups(attrs=None, count=2):
"""Create multiple fake consistency groups.
- :param Dictionary attrs:
+ :param dict attrs:
A dictionary with all attributes
:param int count:
The number of consistency groups to fake
@@ -713,7 +769,7 @@ class FakeConsistencyGroupSnapshot(object):
def create_one_consistency_group_snapshot(attrs=None):
"""Create a fake consistency group snapshot.
- :param Dictionary attrs:
+ :param dict attrs:
A dictionary with all attributes
:return:
A FakeResource object with id, name, description, etc.
@@ -742,7 +798,7 @@ class FakeConsistencyGroupSnapshot(object):
def create_consistency_group_snapshots(attrs=None, count=2):
"""Create multiple fake consistency group snapshots.
- :param Dictionary attrs:
+ :param dict attrs:
A dictionary with all attributes
:param int count:
The number of consistency group snapshots to fake
@@ -789,7 +845,7 @@ class FakeExtension(object):
def create_one_extension(attrs=None):
"""Create a fake extension.
- :param Dictionary attrs:
+ :param dict attrs:
A dictionary with all attributes
:return:
A FakeResource object with name, namespace, etc.
@@ -825,7 +881,7 @@ class FakeQos(object):
def create_one_qos(attrs=None):
"""Create a fake Qos specification.
- :param Dictionary attrs:
+ :param dict attrs:
A dictionary with all attributes
:return:
A FakeResource object with id, name, consumer, etc.
@@ -852,7 +908,7 @@ class FakeQos(object):
def create_one_qos_association(attrs=None):
"""Create a fake Qos specification association.
- :param Dictionary attrs:
+ :param dict attrs:
A dictionary with all attributes
:return:
A FakeResource object with id, name, association_type, etc.
@@ -878,7 +934,7 @@ class FakeQos(object):
def create_qoses(attrs=None, count=2):
"""Create multiple fake Qos specifications.
- :param Dictionary attrs:
+ :param dict attrs:
A dictionary with all attributes
:param int count:
The number of Qos specifications to fake
@@ -920,7 +976,7 @@ class FakeSnapshot(object):
def create_one_snapshot(attrs=None):
"""Create a fake snapshot.
- :param Dictionary attrs:
+ :param dict attrs:
A dictionary with all attributes
:return:
A FakeResource object with id, name, description, etc.
@@ -951,7 +1007,7 @@ class FakeSnapshot(object):
def create_snapshots(attrs=None, count=2):
"""Create multiple fake snapshots.
- :param Dictionary attrs:
+ :param dict attrs:
A dictionary with all attributes
:param int count:
The number of snapshots to fake
@@ -993,9 +1049,9 @@ class FakeVolumeType(object):
def create_one_volume_type(attrs=None, methods=None):
"""Create a fake volume type.
- :param Dictionary attrs:
+ :param dict attrs:
A dictionary with all attributes
- :param Dictionary methods:
+ :param dict methods:
A dictionary with all methods
:return:
A FakeResource object with id, name, description, etc.
@@ -1025,7 +1081,7 @@ class FakeVolumeType(object):
def create_volume_types(attrs=None, count=2):
"""Create multiple fake volume_types.
- :param Dictionary attrs:
+ :param dict attrs:
A dictionary with all attributes
:param int count:
The number of types to fake
@@ -1063,7 +1119,7 @@ class FakeVolumeType(object):
def create_one_encryption_volume_type(attrs=None):
"""Create a fake encryption volume type.
- :param Dictionary attrs:
+ :param dict attrs:
A dictionary with all attributes
:return:
A FakeResource object with volume_type_id etc.
diff --git a/releasenotes/notes/add-network-local-ip-df3a9ce7610d8b90.yaml b/releasenotes/notes/add-network-local-ip-df3a9ce7610d8b90.yaml
new file mode 100644
index 00000000..dbbd3028
--- /dev/null
+++ b/releasenotes/notes/add-network-local-ip-df3a9ce7610d8b90.yaml
@@ -0,0 +1,8 @@
+---
+features:
+ - Add ``local ip create``, ``local ip delete``,
+ ``local ip list``, ``local ip set``, ``local ip show``,
+ ``local ip association create``, ``local ip association delete``
+ and ``local ip association list`` commands to support Neutron Local IP
+ CRUD operations.
+ [`bug 1930200 <https://bugs.launchpad.net/neutron/+bug/1930200>`_]
diff --git a/releasenotes/notes/add-option-to-unset-port-host-c76de9b1d2addf9a.yaml b/releasenotes/notes/add-option-to-unset-port-host-c76de9b1d2addf9a.yaml
new file mode 100644
index 00000000..0aa04760
--- /dev/null
+++ b/releasenotes/notes/add-option-to-unset-port-host-c76de9b1d2addf9a.yaml
@@ -0,0 +1,5 @@
+---
+features:
+ - |
+ Add possibility to unbind Neutron's port from the host by unsetting its
+ host_id.
diff --git a/releasenotes/notes/check-limit-quota-cc7f291dd1b537c1.yaml b/releasenotes/notes/check-limit-quota-cc7f291dd1b537c1.yaml
new file mode 100644
index 00000000..171b4a5a
--- /dev/null
+++ b/releasenotes/notes/check-limit-quota-cc7f291dd1b537c1.yaml
@@ -0,0 +1,5 @@
+---
+features:
+ - Add ``--check-limit`` option to the ``openstack quota set`` command (only
+ for network commands). The network quota engine will check the resource
+ usage before setting the new quota limit.
diff --git a/releasenotes/notes/fix-flavor-in-server-list-microversion-2.47-af200e9bb4747e2d.yaml b/releasenotes/notes/fix-flavor-in-server-list-microversion-2.47-af200e9bb4747e2d.yaml
new file mode 100644
index 00000000..fdb37bbb
--- /dev/null
+++ b/releasenotes/notes/fix-flavor-in-server-list-microversion-2.47-af200e9bb4747e2d.yaml
@@ -0,0 +1,8 @@
+---
+features:
+ - |
+ Add support for compute API microversion 2.47, which changes how flavor
+ details are included in server detail responses. In 2.46 and below,
+ only the flavor ID was shown in the server detail response. Starting in
+ 2.47, flavor information is embedded in the server response. The newer
+ behavior is now supported.
diff --git a/releasenotes/notes/list-subnet-by-pool-id-a642efc13d04fa08.yaml b/releasenotes/notes/list-subnet-by-pool-id-a642efc13d04fa08.yaml
new file mode 100644
index 00000000..d784a9aa
--- /dev/null
+++ b/releasenotes/notes/list-subnet-by-pool-id-a642efc13d04fa08.yaml
@@ -0,0 +1,5 @@
+---
+features:
+ - |
+ Add ``--subnet-pool`` option to ``subnet list`` to filter
+ by subnets by subnet pool.
diff --git a/releasenotes/notes/migrate-add-fixed-ip-to-sdk-3d932d77633bc765.yaml b/releasenotes/notes/migrate-add-fixed-ip-to-sdk-3d932d77633bc765.yaml
new file mode 100644
index 00000000..79899b3e
--- /dev/null
+++ b/releasenotes/notes/migrate-add-fixed-ip-to-sdk-3d932d77633bc765.yaml
@@ -0,0 +1,3 @@
+features:
+ - |
+ Switch the add fixed IP command from novaclient to SDK.
diff --git a/releasenotes/notes/migrate-create-server-image-to-sdk-e3d8077ffe05bb3d.yaml b/releasenotes/notes/migrate-create-server-image-to-sdk-e3d8077ffe05bb3d.yaml
new file mode 100644
index 00000000..20f8d550
--- /dev/null
+++ b/releasenotes/notes/migrate-create-server-image-to-sdk-e3d8077ffe05bb3d.yaml
@@ -0,0 +1,4 @@
+---
+features:
+ - |
+ Migrate openstack server image create from novaclient to sdk.
diff --git a/releasenotes/notes/migrate-server-add-volume-to-sdk-685e036a88839651.yaml b/releasenotes/notes/migrate-server-add-volume-to-sdk-685e036a88839651.yaml
new file mode 100644
index 00000000..54abdacb
--- /dev/null
+++ b/releasenotes/notes/migrate-server-add-volume-to-sdk-685e036a88839651.yaml
@@ -0,0 +1,4 @@
+---
+features:
+ - |
+ Migrate openstack server add volume to using sdk.
diff --git a/releasenotes/notes/migrate-server-pause-unpause-to-sdk-d74ec8536b764af6.yaml b/releasenotes/notes/migrate-server-pause-unpause-to-sdk-d74ec8536b764af6.yaml
new file mode 100644
index 00000000..e2d40034
--- /dev/null
+++ b/releasenotes/notes/migrate-server-pause-unpause-to-sdk-d74ec8536b764af6.yaml
@@ -0,0 +1,5 @@
+---
+features:
+ - |
+ Migrate ``server pause`` and ``server unpause`` commands from novaclient
+ to sdk.
diff --git a/releasenotes/notes/migrate-server-suspend-resume-to-sdk-fd1709336607b496.yaml b/releasenotes/notes/migrate-server-suspend-resume-to-sdk-fd1709336607b496.yaml
new file mode 100644
index 00000000..7d3781bb
--- /dev/null
+++ b/releasenotes/notes/migrate-server-suspend-resume-to-sdk-fd1709336607b496.yaml
@@ -0,0 +1,5 @@
+---
+features:
+ - |
+ Migrate ``server suspend`` and ``server resume`` commands from novaclient
+ to sdk.
diff --git a/releasenotes/notes/migrate-service-list-delete-set-to-sdk-920cbe0d210af565.yaml b/releasenotes/notes/migrate-service-list-delete-set-to-sdk-920cbe0d210af565.yaml
new file mode 100644
index 00000000..55dcb43c
--- /dev/null
+++ b/releasenotes/notes/migrate-service-list-delete-set-to-sdk-920cbe0d210af565.yaml
@@ -0,0 +1,3 @@
+features:
+ - |
+ Switch the compute service commands from novaclient to SDK.
diff --git a/releasenotes/notes/options-create-router-97910a882b604652.yaml b/releasenotes/notes/options-create-router-97910a882b604652.yaml
new file mode 100644
index 00000000..f7d90b75
--- /dev/null
+++ b/releasenotes/notes/options-create-router-97910a882b604652.yaml
@@ -0,0 +1,8 @@
+---
+features:
+ - |
+ It is now possible to add an external gateway to a router
+ immediately on creation. Previously it could only be done by
+ modifying the router after it had been created. This includes
+ the options to en- or disable SNAT and to specify a fixed-ip
+ on the external network.
diff --git a/releasenotes/notes/pass_ssh_args-cf26a2ce26ccddaf.yaml b/releasenotes/notes/pass_ssh_args-cf26a2ce26ccddaf.yaml
new file mode 100644
index 00000000..fe81de94
--- /dev/null
+++ b/releasenotes/notes/pass_ssh_args-cf26a2ce26ccddaf.yaml
@@ -0,0 +1,14 @@
+---
+features:
+ - |
+ Added the ability to pass arguments through to the ``ssh`` command When
+ using ``openstack server ssh``. This allows the user to use any ``ssh``
+ option without needing to add that option to the openstack client.
+ Existing openstackclient options that mirror SSH options are now
+ deprecated.
+deprecations:
+ - |
+ ``openstack server ssh`` options that mirror ``ssh`` options are now
+ deprecated (``--login, -l, --port, --identity, --option, -o, -vz``).
+ The ``ssh`` equivalent of each deprecated option should be used instead.
+ For example ``openstack server ssh instance -- -l user -i key``
diff --git a/releasenotes/notes/port-list-security-group-4af5d2e789174ff9.yaml b/releasenotes/notes/port-list-security-group-4af5d2e789174ff9.yaml
new file mode 100644
index 00000000..c68eeafb
--- /dev/null
+++ b/releasenotes/notes/port-list-security-group-4af5d2e789174ff9.yaml
@@ -0,0 +1,5 @@
+---
+features:
+ - |
+ Added ``--security-group`` option to the ``os port list`` command. This
+ option is appendable and multiple security group IDs can be provided.
diff --git a/releasenotes/notes/switch-server-remove-network-port-to-sdk-829ba711e0e198d5.yaml b/releasenotes/notes/switch-server-remove-network-port-to-sdk-829ba711e0e198d5.yaml
new file mode 100644
index 00000000..6b47b1b3
--- /dev/null
+++ b/releasenotes/notes/switch-server-remove-network-port-to-sdk-829ba711e0e198d5.yaml
@@ -0,0 +1,4 @@
+---
+features:
+ - |
+ Switch server remove volume/port to using sdk.
diff --git a/releasenotes/notes/switch-server-remove-volume-to-sdk-47e9befd2672dcdf.yaml b/releasenotes/notes/switch-server-remove-volume-to-sdk-47e9befd2672dcdf.yaml
new file mode 100644
index 00000000..3e0397d7
--- /dev/null
+++ b/releasenotes/notes/switch-server-remove-volume-to-sdk-47e9befd2672dcdf.yaml
@@ -0,0 +1,4 @@
+---
+features:
+ - |
+ Switch command server remove volume to using sdk.
diff --git a/releasenotes/source/conf.py b/releasenotes/source/conf.py
index 206c0ce2..179f8f23 100644
--- a/releasenotes/source/conf.py
+++ b/releasenotes/source/conf.py
@@ -37,6 +37,14 @@
# Add any Sphinx extension module names here, as strings. They can be
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
# ones.
+from sphinx.util import logging
+
+# According to the discussion in
+# https://github.com/sphinx-doc/sphinx/issues/10112 this may be applied as a
+# dirty hack until the issue with replacing extlinks is resolved
+linklogger = logging.getLogger('sphinx.ext.extlinks')
+linklogger.setLevel(40) # Ignore messages less severe than ERROR
+
extensions = [
'openstackdocstheme',
'reno.sphinxext',
diff --git a/requirements.txt b/requirements.txt
index 0ac991da..cb414ebb 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -5,7 +5,7 @@ pbr!=2.1.0,>=2.0.0 # Apache-2.0
cliff>=3.5.0 # Apache-2.0
iso8601>=0.1.11 # MIT
-openstacksdk>=0.56.0 # Apache-2.0
+openstacksdk>=0.61.0 # Apache-2.0
osc-lib>=2.3.0 # Apache-2.0
oslo.i18n>=3.15.3 # Apache-2.0
oslo.utils>=3.33.0 # Apache-2.0
diff --git a/setup.cfg b/setup.cfg
index cb152038..b4718b1f 100644
--- a/setup.cfg
+++ b/setup.cfg
@@ -415,6 +415,16 @@ openstack.network.v2 =
ip_availability_list = openstackclient.network.v2.ip_availability:ListIPAvailability
ip_availability_show = openstackclient.network.v2.ip_availability:ShowIPAvailability
+ local_ip_create = openstackclient.network.v2.local_ip:CreateLocalIP
+ local_ip_delete = openstackclient.network.v2.local_ip:DeleteLocalIP
+ local_ip_list = openstackclient.network.v2.local_ip:ListLocalIP
+ local_ip_set = openstackclient.network.v2.local_ip:SetLocalIP
+ local_ip_show = openstackclient.network.v2.local_ip:ShowLocalIP
+
+ local_ip_association_create = openstackclient.network.v2.local_ip_association:CreateLocalIPAssociation
+ local_ip_association_delete = openstackclient.network.v2.local_ip_association:DeleteLocalIPAssociation
+ local_ip_association_list = openstackclient.network.v2.local_ip_association:ListLocalIPAssociation
+
network_agent_add_network = openstackclient.network.v2.network_agent:AddNetworkToAgent
network_agent_add_router = openstackclient.network.v2.network_agent:AddRouterToAgent
network_agent_delete = openstackclient.network.v2.network_agent:DeleteNetworkAgent