python-openstackclient 6.6.0__py3-none-any.whl → 7.0.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- openstackclient/api/api.py +7 -8
- openstackclient/api/compute_v2.py +352 -638
- openstackclient/api/image_v1.py +1 -1
- openstackclient/api/object_store_v1.py +3 -4
- openstackclient/common/availability_zone.py +1 -1
- openstackclient/common/clientmanager.py +16 -4
- openstackclient/common/configuration.py +1 -1
- openstackclient/common/extension.py +1 -1
- openstackclient/common/limits.py +66 -32
- openstackclient/common/module.py +3 -3
- openstackclient/common/progressbar.py +2 -2
- openstackclient/common/project_cleanup.py +5 -2
- openstackclient/common/quota.py +281 -410
- openstackclient/common/versions.py +1 -1
- openstackclient/compute/client.py +7 -116
- openstackclient/compute/v2/agent.py +75 -49
- openstackclient/compute/v2/aggregate.py +9 -9
- openstackclient/compute/v2/console.py +2 -2
- openstackclient/compute/v2/flavor.py +6 -6
- openstackclient/compute/v2/host.py +38 -33
- openstackclient/compute/v2/hypervisor.py +4 -3
- openstackclient/compute/v2/keypair.py +7 -8
- openstackclient/compute/v2/server.py +478 -396
- openstackclient/compute/v2/server_backup.py +1 -1
- openstackclient/compute/v2/server_group.py +4 -4
- openstackclient/compute/v2/server_image.py +1 -1
- openstackclient/compute/v2/server_migration.py +3 -4
- openstackclient/compute/v2/service.py +4 -4
- openstackclient/compute/v2/usage.py +3 -3
- openstackclient/identity/common.py +34 -0
- openstackclient/identity/v2_0/catalog.py +2 -2
- openstackclient/identity/v2_0/ec2creds.py +4 -4
- openstackclient/identity/v2_0/endpoint.py +4 -4
- openstackclient/identity/v2_0/project.py +6 -6
- openstackclient/identity/v2_0/role.py +5 -5
- openstackclient/identity/v2_0/role_assignment.py +1 -1
- openstackclient/identity/v2_0/service.py +4 -4
- openstackclient/identity/v2_0/token.py +2 -2
- openstackclient/identity/v2_0/user.py +7 -7
- openstackclient/identity/v3/access_rule.py +3 -3
- openstackclient/identity/v3/application_credential.py +127 -45
- openstackclient/identity/v3/catalog.py +2 -2
- openstackclient/identity/v3/consumer.py +4 -4
- openstackclient/identity/v3/credential.py +5 -5
- openstackclient/identity/v3/domain.py +5 -5
- openstackclient/identity/v3/ec2creds.py +4 -4
- openstackclient/identity/v3/endpoint.py +7 -7
- openstackclient/identity/v3/endpoint_group.py +8 -10
- openstackclient/identity/v3/federation_protocol.py +5 -5
- openstackclient/identity/v3/group.py +8 -8
- openstackclient/identity/v3/identity_provider.py +5 -5
- openstackclient/identity/v3/implied_role.py +3 -3
- openstackclient/identity/v3/limit.py +5 -5
- openstackclient/identity/v3/mapping.py +5 -5
- openstackclient/identity/v3/policy.py +5 -5
- openstackclient/identity/v3/project.py +5 -5
- openstackclient/identity/v3/region.py +5 -5
- openstackclient/identity/v3/registered_limit.py +5 -5
- openstackclient/identity/v3/role.py +7 -7
- openstackclient/identity/v3/role_assignment.py +92 -140
- openstackclient/identity/v3/service.py +64 -34
- openstackclient/identity/v3/service_provider.py +4 -4
- openstackclient/identity/v3/tag.py +2 -2
- openstackclient/identity/v3/token.py +5 -5
- openstackclient/identity/v3/trust.py +3 -3
- openstackclient/identity/v3/user.py +144 -80
- openstackclient/image/client.py +4 -4
- openstackclient/image/v1/image.py +8 -9
- openstackclient/image/v2/cache.py +12 -10
- openstackclient/image/v2/metadef_objects.py +44 -0
- openstackclient/image/v2/metadef_resource_type_association.py +189 -0
- openstackclient/image/v2/task.py +1 -1
- openstackclient/network/common.py +6 -5
- openstackclient/network/utils.py +2 -2
- openstackclient/network/v2/address_group.py +6 -6
- openstackclient/network/v2/address_scope.py +5 -5
- openstackclient/network/v2/default_security_group_rule.py +1 -1
- openstackclient/network/v2/floating_ip.py +8 -10
- openstackclient/network/v2/floating_ip_pool.py +6 -15
- openstackclient/network/v2/floating_ip_port_forwarding.py +5 -13
- openstackclient/network/v2/ip_availability.py +2 -2
- openstackclient/network/v2/l3_conntrack_helper.py +5 -5
- openstackclient/network/v2/network.py +8 -8
- openstackclient/network/v2/network_agent.py +8 -8
- openstackclient/network/v2/network_auto_allocated_topology.py +2 -2
- openstackclient/network/v2/network_flavor.py +6 -8
- openstackclient/network/v2/network_flavor_profile.py +4 -4
- openstackclient/network/v2/network_meter.py +3 -3
- openstackclient/network/v2/network_meter_rule.py +3 -3
- openstackclient/network/v2/network_qos_policy.py +5 -5
- openstackclient/network/v2/network_qos_rule.py +9 -9
- openstackclient/network/v2/network_qos_rule_type.py +1 -1
- openstackclient/network/v2/network_rbac.py +5 -5
- openstackclient/network/v2/network_segment.py +5 -5
- openstackclient/network/v2/network_segment_range.py +7 -7
- openstackclient/network/v2/network_trunk.py +7 -7
- openstackclient/network/v2/port.py +26 -12
- openstackclient/network/v2/router.py +403 -54
- openstackclient/network/v2/security_group.py +18 -14
- openstackclient/network/v2/security_group_rule.py +18 -15
- openstackclient/network/v2/subnet.py +15 -8
- openstackclient/network/v2/subnet_pool.py +6 -6
- openstackclient/object/v1/account.py +2 -2
- openstackclient/object/v1/container.py +7 -7
- openstackclient/object/v1/object.py +7 -7
- openstackclient/shell.py +4 -6
- openstackclient/tests/functional/base.py +1 -1
- openstackclient/tests/functional/common/test_extension.py +1 -1
- openstackclient/tests/functional/common/test_help.py +2 -2
- openstackclient/tests/functional/common/test_module.py +1 -1
- openstackclient/tests/functional/common/test_quota.py +43 -61
- openstackclient/tests/functional/compute/v2/common.py +2 -2
- openstackclient/tests/functional/compute/v2/test_flavor.py +2 -2
- openstackclient/tests/functional/compute/v2/test_keypair.py +1 -1
- openstackclient/tests/functional/compute/v2/test_server.py +5 -5
- openstackclient/tests/functional/compute/v2/test_server_event.py +1 -1
- openstackclient/tests/functional/identity/v2/common.py +3 -3
- openstackclient/tests/functional/identity/v3/common.py +14 -6
- openstackclient/tests/functional/identity/v3/test_application_credential.py +13 -19
- openstackclient/tests/functional/identity/v3/test_domain.py +1 -3
- openstackclient/tests/functional/identity/v3/test_endpoint.py +1 -1
- openstackclient/tests/functional/identity/v3/test_idp.py +1 -1
- openstackclient/tests/functional/identity/v3/test_limit.py +2 -2
- openstackclient/tests/functional/identity/v3/test_region.py +1 -3
- openstackclient/tests/functional/identity/v3/test_registered_limit.py +1 -1
- openstackclient/tests/functional/identity/v3/test_role.py +2 -2
- openstackclient/tests/functional/identity/v3/test_role_assignment.py +210 -0
- openstackclient/tests/functional/identity/v3/test_service.py +4 -6
- openstackclient/tests/functional/identity/v3/test_service_provider.py +1 -3
- openstackclient/tests/functional/image/base.py +1 -1
- openstackclient/tests/functional/image/v2/test_image.py +1 -1
- openstackclient/tests/functional/image/v2/test_info.py +1 -1
- openstackclient/tests/functional/network/v2/common.py +4 -6
- openstackclient/tests/functional/network/v2/test_network.py +5 -3
- openstackclient/tests/functional/network/v2/test_network_agent.py +7 -5
- openstackclient/tests/functional/network/v2/test_network_qos_rule.py +4 -4
- openstackclient/tests/functional/network/v2/test_port.py +11 -7
- openstackclient/tests/functional/network/v2/test_router.py +2 -2
- openstackclient/tests/functional/object/v1/common.py +1 -1
- openstackclient/tests/functional/object/v1/test_container.py +3 -3
- openstackclient/tests/functional/object/v1/test_object.py +9 -13
- openstackclient/tests/functional/volume/base.py +1 -1
- openstackclient/tests/functional/volume/v1/test_service.py +1 -1
- openstackclient/tests/functional/volume/v1/test_snapshot.py +2 -2
- openstackclient/tests/functional/volume/v1/test_transfer_request.py +2 -2
- openstackclient/tests/functional/volume/v1/test_volume_type.py +1 -1
- openstackclient/tests/functional/volume/v2/test_service.py +2 -2
- openstackclient/tests/functional/volume/v2/test_volume_backup.py +2 -2
- openstackclient/tests/functional/volume/v2/test_volume_snapshot.py +2 -2
- openstackclient/tests/functional/volume/v2/test_volume_type.py +1 -1
- openstackclient/tests/functional/volume/v3/test_volume_snapshot.py +2 -2
- openstackclient/tests/functional/volume/v3/test_volume_type.py +1 -1
- openstackclient/tests/unit/api/fakes.py +1 -1
- openstackclient/tests/unit/api/test_api.py +2 -2
- openstackclient/tests/unit/api/test_compute_v2.py +522 -707
- openstackclient/tests/unit/api/test_image_v1.py +1 -1
- openstackclient/tests/unit/api/test_image_v2.py +1 -1
- openstackclient/tests/unit/api/test_object_store_v1.py +4 -4
- openstackclient/tests/unit/common/test_limits.py +73 -35
- openstackclient/tests/unit/common/test_logs.py +2 -2
- openstackclient/tests/unit/common/test_module.py +4 -2
- openstackclient/tests/unit/common/test_project_cleanup.py +31 -6
- openstackclient/tests/unit/common/test_quota.py +490 -630
- openstackclient/tests/unit/compute/v2/fakes.py +37 -286
- openstackclient/tests/unit/compute/v2/test_agent.py +189 -147
- openstackclient/tests/unit/compute/v2/test_aggregate.py +18 -16
- openstackclient/tests/unit/compute/v2/test_console.py +4 -5
- openstackclient/tests/unit/compute/v2/test_flavor.py +59 -68
- openstackclient/tests/unit/compute/v2/test_host.py +83 -54
- openstackclient/tests/unit/compute/v2/test_hypervisor.py +28 -31
- openstackclient/tests/unit/compute/v2/test_hypervisor_stats.py +2 -2
- openstackclient/tests/unit/compute/v2/test_keypair.py +65 -50
- openstackclient/tests/unit/compute/v2/test_server.py +2895 -2459
- openstackclient/tests/unit/compute/v2/test_server_backup.py +1 -1
- openstackclient/tests/unit/compute/v2/test_server_event.py +14 -39
- openstackclient/tests/unit/compute/v2/test_server_group.py +28 -29
- openstackclient/tests/unit/compute/v2/test_server_migration.py +43 -68
- openstackclient/tests/unit/compute/v2/test_server_volume.py +17 -34
- openstackclient/tests/unit/compute/v2/test_service.py +34 -52
- openstackclient/tests/unit/compute/v2/test_usage.py +4 -4
- openstackclient/tests/unit/fakes.py +11 -11
- openstackclient/tests/unit/identity/v2_0/fakes.py +27 -10
- openstackclient/tests/unit/identity/v2_0/test_catalog.py +3 -3
- openstackclient/tests/unit/identity/v2_0/test_endpoint.py +7 -7
- openstackclient/tests/unit/identity/v2_0/test_project.py +8 -8
- openstackclient/tests/unit/identity/v2_0/test_role.py +10 -10
- openstackclient/tests/unit/identity/v2_0/test_role_assignment.py +4 -4
- openstackclient/tests/unit/identity/v2_0/test_service.py +6 -6
- openstackclient/tests/unit/identity/v2_0/test_token.py +4 -4
- openstackclient/tests/unit/identity/v2_0/test_user.py +8 -8
- openstackclient/tests/unit/identity/v3/fakes.py +59 -20
- openstackclient/tests/unit/identity/v3/test_access_rule.py +5 -5
- openstackclient/tests/unit/identity/v3/test_application_credential.py +212 -235
- openstackclient/tests/unit/identity/v3/test_catalog.py +3 -3
- openstackclient/tests/unit/identity/v3/test_consumer.py +7 -8
- openstackclient/tests/unit/identity/v3/test_credential.py +9 -9
- openstackclient/tests/unit/identity/v3/test_domain.py +8 -8
- openstackclient/tests/unit/identity/v3/test_endpoint.py +13 -13
- openstackclient/tests/unit/identity/v3/test_endpoint_group.py +12 -14
- openstackclient/tests/unit/identity/v3/test_group.py +12 -12
- openstackclient/tests/unit/identity/v3/test_identity_provider.py +8 -8
- openstackclient/tests/unit/identity/v3/test_implied_role.py +5 -5
- openstackclient/tests/unit/identity/v3/test_limit.py +7 -7
- openstackclient/tests/unit/identity/v3/test_mappings.py +7 -7
- openstackclient/tests/unit/identity/v3/test_oauth.py +5 -5
- openstackclient/tests/unit/identity/v3/test_project.py +16 -16
- openstackclient/tests/unit/identity/v3/test_protocol.py +7 -7
- openstackclient/tests/unit/identity/v3/test_region.py +7 -7
- openstackclient/tests/unit/identity/v3/test_registered_limit.py +12 -13
- openstackclient/tests/unit/identity/v3/test_role.py +13 -13
- openstackclient/tests/unit/identity/v3/test_role_assignment.py +410 -331
- openstackclient/tests/unit/identity/v3/test_service.py +93 -97
- openstackclient/tests/unit/identity/v3/test_service_provider.py +7 -7
- openstackclient/tests/unit/identity/v3/test_token.py +4 -4
- openstackclient/tests/unit/identity/v3/test_trust.py +9 -9
- openstackclient/tests/unit/identity/v3/test_unscoped_saml.py +4 -4
- openstackclient/tests/unit/identity/v3/test_user.py +299 -327
- openstackclient/tests/unit/image/v1/test_image.py +6 -6
- openstackclient/tests/unit/image/v2/fakes.py +46 -9
- openstackclient/tests/unit/image/v2/test_cache.py +2 -2
- openstackclient/tests/unit/image/v2/test_image.py +3 -3
- openstackclient/tests/unit/image/v2/test_metadef_objects.py +62 -0
- openstackclient/tests/unit/image/v2/test_metadef_resource_type_association.py +131 -0
- openstackclient/tests/unit/integ/base.py +1 -1
- openstackclient/tests/unit/integ/cli/test_project.py +4 -4
- openstackclient/tests/unit/integ/cli/test_shell.py +7 -7
- openstackclient/tests/unit/network/test_common.py +12 -21
- openstackclient/tests/unit/network/v2/fakes.py +64 -130
- openstackclient/tests/unit/network/v2/test_address_group.py +15 -15
- openstackclient/tests/unit/network/v2/test_address_scope.py +13 -13
- openstackclient/tests/unit/network/v2/test_default_security_group_rule.py +49 -27
- openstackclient/tests/unit/network/v2/test_floating_ip_compute.py +40 -38
- openstackclient/tests/unit/network/v2/test_floating_ip_network.py +15 -15
- openstackclient/tests/unit/network/v2/test_floating_ip_pool_compute.py +4 -7
- openstackclient/tests/unit/network/v2/test_floating_ip_pool_network.py +3 -5
- openstackclient/tests/unit/network/v2/test_floating_ip_port_forwarding.py +11 -11
- openstackclient/tests/unit/network/v2/test_ip_availability.py +6 -6
- openstackclient/tests/unit/network/v2/test_l3_conntrack_helper.py +11 -21
- openstackclient/tests/unit/network/v2/test_local_ip.py +7 -7
- openstackclient/tests/unit/network/v2/test_local_ip_association.py +3 -5
- openstackclient/tests/unit/network/v2/test_ndp_proxy.py +13 -13
- openstackclient/tests/unit/network/v2/test_network.py +23 -28
- openstackclient/tests/unit/network/v2/test_network_agent.py +17 -21
- openstackclient/tests/unit/network/v2/test_network_auto_allocated_topology.py +8 -8
- openstackclient/tests/unit/network/v2/test_network_compute.py +66 -65
- openstackclient/tests/unit/network/v2/test_network_flavor.py +17 -19
- openstackclient/tests/unit/network/v2/test_network_flavor_profile.py +13 -13
- openstackclient/tests/unit/network/v2/test_network_meter.py +11 -11
- openstackclient/tests/unit/network/v2/test_network_meter_rule.py +11 -11
- openstackclient/tests/unit/network/v2/test_network_qos_policy.py +11 -21
- openstackclient/tests/unit/network/v2/test_network_qos_rule.py +51 -77
- openstackclient/tests/unit/network/v2/test_network_qos_rule_type.py +5 -9
- openstackclient/tests/unit/network/v2/test_network_rbac.py +12 -12
- openstackclient/tests/unit/network/v2/test_network_segment.py +11 -15
- openstackclient/tests/unit/network/v2/test_network_segment_range.py +11 -13
- openstackclient/tests/unit/network/v2/test_network_service_provider.py +3 -5
- openstackclient/tests/unit/network/v2/test_network_trunk.py +11 -11
- openstackclient/tests/unit/network/v2/test_port.py +22 -25
- openstackclient/tests/unit/network/v2/test_router.py +721 -51
- openstackclient/tests/unit/network/v2/test_security_group_compute.py +65 -49
- openstackclient/tests/unit/network/v2/test_security_group_network.py +15 -15
- openstackclient/tests/unit/network/v2/test_security_group_rule_compute.py +57 -45
- openstackclient/tests/unit/network/v2/test_security_group_rule_network.py +11 -19
- openstackclient/tests/unit/network/v2/test_subnet.py +29 -25
- openstackclient/tests/unit/network/v2/test_subnet_pool.py +15 -15
- openstackclient/tests/unit/object/v1/fakes.py +1 -1
- openstackclient/tests/unit/object/v1/test_container.py +5 -5
- openstackclient/tests/unit/object/v1/test_container_all.py +6 -6
- openstackclient/tests/unit/object/v1/test_object.py +3 -3
- openstackclient/tests/unit/object/v1/test_object_all.py +5 -5
- openstackclient/tests/unit/test_shell.py +5 -5
- openstackclient/tests/unit/utils.py +4 -1
- openstackclient/tests/unit/volume/test_find_resource.py +2 -2
- openstackclient/tests/unit/volume/v1/fakes.py +5 -6
- openstackclient/tests/unit/volume/v1/test_volume.py +5 -4
- openstackclient/tests/unit/volume/v2/fakes.py +39 -259
- openstackclient/tests/unit/volume/v2/test_consistency_group_snapshot.py +5 -5
- openstackclient/tests/unit/volume/v2/test_qos_specs.py +9 -9
- openstackclient/tests/unit/volume/v2/test_volume.py +21 -87
- openstackclient/tests/unit/volume/v2/test_volume_backup.py +7 -368
- openstackclient/tests/unit/volume/v2/test_volume_snapshot.py +1 -1
- openstackclient/tests/unit/volume/v2/test_volume_transfer_request.py +0 -44
- openstackclient/tests/unit/volume/v2/test_volume_type.py +6 -87
- openstackclient/tests/unit/volume/v3/fakes.py +505 -22
- openstackclient/tests/unit/volume/v3/test_block_storage_cleanup.py +2 -3
- openstackclient/tests/unit/volume/v3/test_block_storage_cluster.py +10 -11
- openstackclient/tests/unit/volume/v3/test_block_storage_log_level.py +10 -6
- openstackclient/tests/unit/volume/v3/test_block_storage_manage.py +25 -17
- openstackclient/tests/unit/volume/v3/test_block_storage_resource_filter.py +6 -32
- openstackclient/tests/unit/volume/v3/test_service.py +271 -0
- openstackclient/tests/unit/volume/v3/test_volume.py +2177 -33
- openstackclient/tests/unit/volume/v3/test_volume_attachment.py +48 -52
- openstackclient/tests/unit/volume/v3/test_volume_backup.py +892 -0
- openstackclient/tests/unit/volume/v3/test_volume_group.py +19 -20
- openstackclient/tests/unit/volume/v3/test_volume_group_snapshot.py +14 -34
- openstackclient/tests/unit/volume/v3/test_volume_group_type.py +13 -16
- openstackclient/tests/unit/volume/v3/test_volume_message.py +10 -11
- openstackclient/tests/unit/volume/v3/test_volume_snapshot.py +161 -0
- openstackclient/tests/unit/volume/v3/test_volume_transfer_request.py +425 -0
- openstackclient/tests/unit/volume/v3/test_volume_type.py +1109 -0
- openstackclient/volume/v1/qos_specs.py +7 -7
- openstackclient/volume/v1/service.py +2 -2
- openstackclient/volume/v1/volume.py +12 -12
- openstackclient/volume/v1/volume_backup.py +7 -7
- openstackclient/volume/v1/volume_snapshot.py +8 -8
- openstackclient/volume/v1/volume_transfer_request.py +5 -5
- openstackclient/volume/v1/volume_type.py +7 -7
- openstackclient/volume/v2/backup_record.py +2 -2
- openstackclient/volume/v2/consistency_group.py +7 -9
- openstackclient/volume/v2/consistency_group_snapshot.py +4 -12
- openstackclient/volume/v2/qos_specs.py +7 -7
- openstackclient/volume/v2/service.py +2 -2
- openstackclient/volume/v2/volume.py +80 -80
- openstackclient/volume/v2/volume_backend.py +2 -2
- openstackclient/volume/v2/volume_backup.py +7 -217
- openstackclient/volume/v2/volume_host.py +2 -2
- openstackclient/volume/v2/volume_snapshot.py +8 -8
- openstackclient/volume/v2/volume_transfer_request.py +5 -37
- openstackclient/volume/v2/volume_type.py +7 -89
- openstackclient/volume/v3/service.py +56 -0
- openstackclient/volume/v3/volume.py +971 -0
- openstackclient/volume/v3/volume_attachment.py +31 -29
- openstackclient/volume/v3/volume_backup.py +670 -0
- openstackclient/volume/v3/volume_message.py +1 -1
- openstackclient/volume/v3/volume_snapshot.py +97 -0
- openstackclient/volume/v3/volume_transfer_request.py +233 -0
- openstackclient/volume/v3/volume_type.py +967 -0
- {python_openstackclient-6.6.0.dist-info → python_openstackclient-7.0.0.dist-info}/AUTHORS +4 -0
- {python_openstackclient-6.6.0.dist-info → python_openstackclient-7.0.0.dist-info}/METADATA +3 -3
- python_openstackclient-7.0.0.dist-info/RECORD +502 -0
- {python_openstackclient-6.6.0.dist-info → python_openstackclient-7.0.0.dist-info}/entry_points.txt +33 -27
- python_openstackclient-7.0.0.dist-info/pbr.json +1 -0
- python_openstackclient-6.6.0.dist-info/RECORD +0 -489
- python_openstackclient-6.6.0.dist-info/pbr.json +0 -1
- {python_openstackclient-6.6.0.dist-info → python_openstackclient-7.0.0.dist-info}/LICENSE +0 -0
- {python_openstackclient-6.6.0.dist-info → python_openstackclient-7.0.0.dist-info}/WHEEL +0 -0
- {python_openstackclient-6.6.0.dist-info → python_openstackclient-7.0.0.dist-info}/top_level.txt +0 -0
|
@@ -13,6 +13,8 @@
|
|
|
13
13
|
|
|
14
14
|
"""Router action implementations"""
|
|
15
15
|
|
|
16
|
+
import argparse
|
|
17
|
+
import collections
|
|
16
18
|
import copy
|
|
17
19
|
import json
|
|
18
20
|
import logging
|
|
@@ -85,8 +87,120 @@ def _get_columns(item):
|
|
|
85
87
|
)
|
|
86
88
|
|
|
87
89
|
|
|
90
|
+
def is_multiple_gateways_supported(n_client):
|
|
91
|
+
return n_client.find_extension("external-gateway-multihoming") is not None
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
def _passed_multiple_gateways(extension_supported, external_gateways):
|
|
95
|
+
passed_multiple_gws = len(external_gateways) > 1
|
|
96
|
+
if passed_multiple_gws and not extension_supported:
|
|
97
|
+
msg = _(
|
|
98
|
+
'Supplying --external-gateway option multiple times is not '
|
|
99
|
+
'supported due to the lack of external-gateway-multihoming '
|
|
100
|
+
'extension at the Neutron side.'
|
|
101
|
+
)
|
|
102
|
+
raise exceptions.CommandError(msg)
|
|
103
|
+
return passed_multiple_gws
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
def _get_external_gateway_attrs(client_manager, parsed_args):
|
|
107
|
+
attrs = {}
|
|
108
|
+
|
|
109
|
+
if parsed_args.external_gateways:
|
|
110
|
+
external_gateways: collections.defaultdict[str, list[dict]] = (
|
|
111
|
+
collections.defaultdict(list)
|
|
112
|
+
)
|
|
113
|
+
n_client = client_manager.network
|
|
114
|
+
first_network_id = None
|
|
115
|
+
|
|
116
|
+
for gw_net_name_or_id in parsed_args.external_gateways:
|
|
117
|
+
gateway_info = {}
|
|
118
|
+
gw_net = n_client.find_network(
|
|
119
|
+
gw_net_name_or_id, ignore_missing=False
|
|
120
|
+
)
|
|
121
|
+
if first_network_id is None:
|
|
122
|
+
first_network_id = gw_net.id
|
|
123
|
+
gateway_info['network_id'] = gw_net.id
|
|
124
|
+
if 'disable_snat' in parsed_args and parsed_args.disable_snat:
|
|
125
|
+
gateway_info['enable_snat'] = False
|
|
126
|
+
if 'enable_snat' in parsed_args and parsed_args.enable_snat:
|
|
127
|
+
gateway_info['enable_snat'] = True
|
|
128
|
+
|
|
129
|
+
# This option was added before multiple gateways were supported, so
|
|
130
|
+
# it does not have a per-gateway port granularity so just pass it
|
|
131
|
+
# along in gw info in case it is specified.
|
|
132
|
+
if 'qos_policy' in parsed_args and parsed_args.qos_policy:
|
|
133
|
+
qos_id = n_client.find_qos_policy(
|
|
134
|
+
parsed_args.qos_policy, ignore_missing=False
|
|
135
|
+
).id
|
|
136
|
+
gateway_info['qos_policy_id'] = qos_id
|
|
137
|
+
if 'no_qos_policy' in parsed_args and parsed_args.no_qos_policy:
|
|
138
|
+
gateway_info['qos_policy_id'] = None
|
|
139
|
+
|
|
140
|
+
external_gateways[gw_net.id].append(gateway_info)
|
|
141
|
+
|
|
142
|
+
multiple_gws_supported = is_multiple_gateways_supported(n_client)
|
|
143
|
+
# Parse the external fixed IP specs and match them to specific gateway
|
|
144
|
+
# ports if needed.
|
|
145
|
+
if parsed_args.fixed_ips:
|
|
146
|
+
for ip_spec in parsed_args.fixed_ips:
|
|
147
|
+
# If there is only one gateway, this value will represent the
|
|
148
|
+
# network ID for it, otherwise it will be overridden.
|
|
149
|
+
ip_net_id = first_network_id
|
|
150
|
+
|
|
151
|
+
if ip_spec.get('subnet', False):
|
|
152
|
+
subnet_name_id = ip_spec.pop('subnet')
|
|
153
|
+
if subnet_name_id:
|
|
154
|
+
subnet = n_client.find_subnet(
|
|
155
|
+
subnet_name_id, ignore_missing=False
|
|
156
|
+
)
|
|
157
|
+
ip_spec['subnet_id'] = subnet.id
|
|
158
|
+
ip_net_id = subnet.network_id
|
|
159
|
+
if ip_spec.get('ip-address', False):
|
|
160
|
+
ip_spec['ip_address'] = ip_spec.pop('ip-address')
|
|
161
|
+
# Finally, add an ip_spec to the specific gateway identified
|
|
162
|
+
# by a network from the spec.
|
|
163
|
+
if (
|
|
164
|
+
'subnet_id' in ip_spec
|
|
165
|
+
and ip_net_id not in external_gateways
|
|
166
|
+
):
|
|
167
|
+
msg = _(
|
|
168
|
+
'Subnet %s does not belong to any of the networks '
|
|
169
|
+
'provided for --external-gateway.'
|
|
170
|
+
) % (ip_spec['subnet_id'])
|
|
171
|
+
raise exceptions.CommandError(msg)
|
|
172
|
+
for gw_info in external_gateways[ip_net_id]:
|
|
173
|
+
if 'external_fixed_ips' not in gw_info:
|
|
174
|
+
gw_info['external_fixed_ips'] = [ip_spec]
|
|
175
|
+
break
|
|
176
|
+
else:
|
|
177
|
+
# The end user has requested more fixed IPs than there are
|
|
178
|
+
# gateways, add multiple fixed IPs to single gateway to
|
|
179
|
+
# retain current behavior.
|
|
180
|
+
for gw_info in external_gateways[ip_net_id]:
|
|
181
|
+
gw_info['external_fixed_ips'].append(ip_spec)
|
|
182
|
+
break
|
|
183
|
+
|
|
184
|
+
# Use the newer API whenever it is supported regardless of whether one
|
|
185
|
+
# or multiple gateways are passed as arguments.
|
|
186
|
+
if multiple_gws_supported:
|
|
187
|
+
gateway_list = []
|
|
188
|
+
# Now merge the per-network-id lists of external gateway info
|
|
189
|
+
# dicts into one list.
|
|
190
|
+
for gw_info_list in external_gateways.values():
|
|
191
|
+
gateway_list.extend(gw_info_list)
|
|
192
|
+
attrs['external_gateways'] = gateway_list
|
|
193
|
+
else:
|
|
194
|
+
attrs['external_gateway_info'] = external_gateways[
|
|
195
|
+
first_network_id
|
|
196
|
+
][0]
|
|
197
|
+
return attrs
|
|
198
|
+
|
|
199
|
+
|
|
88
200
|
def _get_attrs(client_manager, parsed_args):
|
|
89
201
|
attrs = {}
|
|
202
|
+
n_client = client_manager.network
|
|
203
|
+
|
|
90
204
|
if parsed_args.name is not None:
|
|
91
205
|
attrs['name'] = parsed_args.name
|
|
92
206
|
if parsed_args.enable:
|
|
@@ -113,44 +227,85 @@ def _get_attrs(client_manager, parsed_args):
|
|
|
113
227
|
parsed_args.project_domain,
|
|
114
228
|
).id
|
|
115
229
|
attrs['project_id'] = project_id
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
network = n_client.find_network(
|
|
120
|
-
parsed_args.external_gateway, ignore_missing=False
|
|
121
|
-
)
|
|
122
|
-
gateway_info['network_id'] = network.id
|
|
123
|
-
if parsed_args.disable_snat:
|
|
124
|
-
gateway_info['enable_snat'] = False
|
|
125
|
-
if parsed_args.enable_snat:
|
|
126
|
-
gateway_info['enable_snat'] = True
|
|
127
|
-
if parsed_args.fixed_ip:
|
|
128
|
-
ips = []
|
|
129
|
-
for ip_spec in parsed_args.fixed_ip:
|
|
130
|
-
if ip_spec.get('subnet', False):
|
|
131
|
-
subnet_name_id = ip_spec.pop('subnet')
|
|
132
|
-
if subnet_name_id:
|
|
133
|
-
subnet = n_client.find_subnet(
|
|
134
|
-
subnet_name_id, ignore_missing=False
|
|
135
|
-
)
|
|
136
|
-
ip_spec['subnet_id'] = subnet.id
|
|
137
|
-
if ip_spec.get('ip-address', False):
|
|
138
|
-
ip_spec['ip_address'] = ip_spec.pop('ip-address')
|
|
139
|
-
ips.append(ip_spec)
|
|
140
|
-
gateway_info['external_fixed_ips'] = ips
|
|
141
|
-
attrs['external_gateway_info'] = gateway_info
|
|
230
|
+
|
|
231
|
+
attrs.update(_get_external_gateway_attrs(client_manager, parsed_args))
|
|
232
|
+
|
|
142
233
|
# "router set" command doesn't support setting flavor_id.
|
|
143
234
|
if 'flavor_id' in parsed_args and parsed_args.flavor_id is not None:
|
|
144
|
-
|
|
235
|
+
flavor = n_client.find_flavor(parsed_args.flavor_id)
|
|
236
|
+
attrs['flavor_id'] = flavor.id
|
|
237
|
+
|
|
238
|
+
for attr in ('enable_default_route_bfd', 'enable_default_route_ecmp'):
|
|
239
|
+
value = getattr(parsed_args, attr, None)
|
|
240
|
+
if value is not None:
|
|
241
|
+
attrs[attr] = value
|
|
145
242
|
|
|
146
243
|
return attrs
|
|
147
244
|
|
|
148
245
|
|
|
246
|
+
def _parser_add_bfd_ecmp_arguments(parser):
|
|
247
|
+
"""Helper to add BFD and ECMP args for CreateRouter and SetRouter."""
|
|
248
|
+
parser.add_argument(
|
|
249
|
+
'--enable-default-route-bfd',
|
|
250
|
+
dest='enable_default_route_bfd',
|
|
251
|
+
default=None,
|
|
252
|
+
action='store_true',
|
|
253
|
+
help=_(
|
|
254
|
+
"Enable BFD sessions for default routes inferred from "
|
|
255
|
+
"the external gateway port subnets for this router."
|
|
256
|
+
),
|
|
257
|
+
)
|
|
258
|
+
parser.add_argument(
|
|
259
|
+
'--disable-default-route-bfd',
|
|
260
|
+
dest='enable_default_route_bfd',
|
|
261
|
+
default=None,
|
|
262
|
+
action='store_false',
|
|
263
|
+
help=_(
|
|
264
|
+
"Disable BFD sessions for default routes inferred from "
|
|
265
|
+
"the external gateway port subnets for this router."
|
|
266
|
+
),
|
|
267
|
+
)
|
|
268
|
+
parser.add_argument(
|
|
269
|
+
'--enable-default-route-ecmp',
|
|
270
|
+
dest='enable_default_route_ecmp',
|
|
271
|
+
default=None,
|
|
272
|
+
action='store_true',
|
|
273
|
+
help=_(
|
|
274
|
+
"Add ECMP default routes if multiple are available via "
|
|
275
|
+
"different gateway ports."
|
|
276
|
+
),
|
|
277
|
+
)
|
|
278
|
+
parser.add_argument(
|
|
279
|
+
'--disable-default-route-ecmp',
|
|
280
|
+
dest='enable_default_route_ecmp',
|
|
281
|
+
default=None,
|
|
282
|
+
action='store_false',
|
|
283
|
+
help=_("Add default route only for first gateway port."),
|
|
284
|
+
)
|
|
285
|
+
|
|
286
|
+
|
|
287
|
+
def _command_check_bfd_ecmp_supported(attrs, client):
|
|
288
|
+
"""Helper to check for server side support when bfd/ecmp attrs provided.
|
|
289
|
+
|
|
290
|
+
:raises: exceptions.CommandError
|
|
291
|
+
"""
|
|
292
|
+
if (
|
|
293
|
+
'enable_default_route_bfd' in attrs
|
|
294
|
+
or 'enable_default_route_ecmp' in attrs
|
|
295
|
+
) and not is_multiple_gateways_supported(client):
|
|
296
|
+
msg = _(
|
|
297
|
+
'The external-gateway-multihoming extension is not enabled at '
|
|
298
|
+
'the Neutron side, cannot use --enable-default-route-bfd or '
|
|
299
|
+
'--enable-default-route-ecmp arguments.'
|
|
300
|
+
)
|
|
301
|
+
raise exceptions.CommandError(msg)
|
|
302
|
+
|
|
303
|
+
|
|
149
304
|
class AddPortToRouter(command.Command):
|
|
150
305
|
_description = _("Add a port to a router")
|
|
151
306
|
|
|
152
307
|
def get_parser(self, prog_name):
|
|
153
|
-
parser = super(
|
|
308
|
+
parser = super().get_parser(prog_name)
|
|
154
309
|
parser.add_argument(
|
|
155
310
|
'router',
|
|
156
311
|
metavar='<router>',
|
|
@@ -174,7 +329,7 @@ class AddSubnetToRouter(command.Command):
|
|
|
174
329
|
_description = _("Add a subnet to a router")
|
|
175
330
|
|
|
176
331
|
def get_parser(self, prog_name):
|
|
177
|
-
parser = super(
|
|
332
|
+
parser = super().get_parser(prog_name)
|
|
178
333
|
parser.add_argument(
|
|
179
334
|
'router',
|
|
180
335
|
metavar='<router>',
|
|
@@ -200,7 +355,7 @@ class AddExtraRoutesToRouter(command.ShowOne):
|
|
|
200
355
|
_description = _("Add extra static routes to a router's routing table.")
|
|
201
356
|
|
|
202
357
|
def get_parser(self, prog_name):
|
|
203
|
-
parser = super(
|
|
358
|
+
parser = super().get_parser(prog_name)
|
|
204
359
|
parser.add_argument(
|
|
205
360
|
'router',
|
|
206
361
|
metavar='<router>',
|
|
@@ -251,7 +406,7 @@ class RemoveExtraRoutesFromRouter(command.ShowOne):
|
|
|
251
406
|
)
|
|
252
407
|
|
|
253
408
|
def get_parser(self, prog_name):
|
|
254
|
-
parser = super(
|
|
409
|
+
parser = super().get_parser(prog_name)
|
|
255
410
|
parser.add_argument(
|
|
256
411
|
'router',
|
|
257
412
|
metavar='<router>',
|
|
@@ -302,7 +457,7 @@ class CreateRouter(command.ShowOne, common.NeutronCommandWithExtraArgs):
|
|
|
302
457
|
_description = _("Create a new router")
|
|
303
458
|
|
|
304
459
|
def get_parser(self, prog_name):
|
|
305
|
-
parser = super(
|
|
460
|
+
parser = super().get_parser(prog_name)
|
|
306
461
|
parser.add_argument(
|
|
307
462
|
'name', metavar='<name>', help=_("New router name")
|
|
308
463
|
)
|
|
@@ -362,18 +517,25 @@ class CreateRouter(command.ShowOne, common.NeutronCommandWithExtraArgs):
|
|
|
362
517
|
parser.add_argument(
|
|
363
518
|
'--external-gateway',
|
|
364
519
|
metavar="<network>",
|
|
365
|
-
|
|
520
|
+
action='append',
|
|
521
|
+
help=_(
|
|
522
|
+
"External Network used as router's gateway (name or ID). "
|
|
523
|
+
"(repeat option to set multiple gateways per router "
|
|
524
|
+
"if the L3 service plugin in use supports it)."
|
|
525
|
+
),
|
|
526
|
+
dest='external_gateways',
|
|
366
527
|
)
|
|
367
528
|
parser.add_argument(
|
|
368
529
|
'--fixed-ip',
|
|
369
530
|
metavar='subnet=<subnet>,ip-address=<ip-address>',
|
|
370
531
|
action=parseractions.MultiKeyValueAction,
|
|
371
532
|
optional_keys=['subnet', 'ip-address'],
|
|
533
|
+
dest='fixed_ips',
|
|
372
534
|
help=_(
|
|
373
535
|
"Desired IP and/or subnet (name or ID) "
|
|
374
536
|
"on external gateway: "
|
|
375
537
|
"subnet=<subnet>,ip-address=<ip-address> "
|
|
376
|
-
"(repeat option to set multiple fixed IP addresses)"
|
|
538
|
+
"(repeat option to set multiple fixed IP addresses)."
|
|
377
539
|
),
|
|
378
540
|
)
|
|
379
541
|
snat_group = parser.add_mutually_exclusive_group()
|
|
@@ -402,11 +564,17 @@ class CreateRouter(command.ShowOne, common.NeutronCommandWithExtraArgs):
|
|
|
402
564
|
action='store_false',
|
|
403
565
|
help=_("Disable IPv6 NDP proxy on external gateway"),
|
|
404
566
|
)
|
|
567
|
+
parser.add_argument(
|
|
568
|
+
'--flavor',
|
|
569
|
+
metavar='<flavor-id>',
|
|
570
|
+
help=_("Associate the router to a flavor (by name or ID"),
|
|
571
|
+
)
|
|
405
572
|
parser.add_argument(
|
|
406
573
|
'--flavor-id',
|
|
407
574
|
metavar='<flavor-id>',
|
|
408
|
-
help=
|
|
575
|
+
help=argparse.SUPPRESS,
|
|
409
576
|
)
|
|
577
|
+
_parser_add_bfd_ecmp_arguments(parser)
|
|
410
578
|
|
|
411
579
|
return parser
|
|
412
580
|
|
|
@@ -422,7 +590,7 @@ class CreateRouter(command.ShowOne, common.NeutronCommandWithExtraArgs):
|
|
|
422
590
|
self._parse_extra_properties(parsed_args.extra_properties)
|
|
423
591
|
)
|
|
424
592
|
|
|
425
|
-
if parsed_args.enable_ndp_proxy and not parsed_args.
|
|
593
|
+
if parsed_args.enable_ndp_proxy and not parsed_args.external_gateways:
|
|
426
594
|
msg = _(
|
|
427
595
|
"You must specify '--external-gateway' in order "
|
|
428
596
|
"to enable router's NDP proxy"
|
|
@@ -432,15 +600,26 @@ class CreateRouter(command.ShowOne, common.NeutronCommandWithExtraArgs):
|
|
|
432
600
|
if parsed_args.enable_ndp_proxy is not None:
|
|
433
601
|
attrs['enable_ndp_proxy'] = parsed_args.enable_ndp_proxy
|
|
434
602
|
|
|
603
|
+
_command_check_bfd_ecmp_supported(attrs, client)
|
|
604
|
+
|
|
605
|
+
external_gateways = attrs.pop('external_gateways', None)
|
|
435
606
|
obj = client.create_router(**attrs)
|
|
436
607
|
# tags cannot be set when created, so tags need to be set later.
|
|
437
608
|
_tag.update_tags_for_set(client, obj, parsed_args)
|
|
438
609
|
|
|
610
|
+
# If the multiple external gateways API is intended to be used,
|
|
611
|
+
# do a separate API call to set the desired external gateways as the
|
|
612
|
+
# router creation API supports adding only one.
|
|
613
|
+
if external_gateways:
|
|
614
|
+
client.update_external_gateways(
|
|
615
|
+
obj, body={'router': {'external_gateways': external_gateways}}
|
|
616
|
+
)
|
|
617
|
+
|
|
439
618
|
if (
|
|
440
619
|
parsed_args.disable_snat
|
|
441
620
|
or parsed_args.enable_snat
|
|
442
|
-
or parsed_args.
|
|
443
|
-
) and not parsed_args.
|
|
621
|
+
or parsed_args.fixed_ips
|
|
622
|
+
) and not parsed_args.external_gateways:
|
|
444
623
|
msg = _(
|
|
445
624
|
"You must specify '--external-gateway' in order "
|
|
446
625
|
"to specify SNAT or fixed-ip values"
|
|
@@ -457,7 +636,7 @@ class DeleteRouter(command.Command):
|
|
|
457
636
|
_description = _("Delete router(s)")
|
|
458
637
|
|
|
459
638
|
def get_parser(self, prog_name):
|
|
460
|
-
parser = super(
|
|
639
|
+
parser = super().get_parser(prog_name)
|
|
461
640
|
parser.add_argument(
|
|
462
641
|
'router',
|
|
463
642
|
metavar="<router>",
|
|
@@ -499,7 +678,7 @@ class ListRouter(command.Lister):
|
|
|
499
678
|
_description = _("List routers")
|
|
500
679
|
|
|
501
680
|
def get_parser(self, prog_name):
|
|
502
|
-
parser = super(
|
|
681
|
+
parser = super().get_parser(prog_name)
|
|
503
682
|
parser.add_argument(
|
|
504
683
|
'--name',
|
|
505
684
|
metavar='<name>',
|
|
@@ -643,7 +822,7 @@ class RemovePortFromRouter(command.Command):
|
|
|
643
822
|
_description = _("Remove a port from a router")
|
|
644
823
|
|
|
645
824
|
def get_parser(self, prog_name):
|
|
646
|
-
parser = super(
|
|
825
|
+
parser = super().get_parser(prog_name)
|
|
647
826
|
parser.add_argument(
|
|
648
827
|
'router',
|
|
649
828
|
metavar='<router>',
|
|
@@ -669,7 +848,7 @@ class RemoveSubnetFromRouter(command.Command):
|
|
|
669
848
|
_description = _("Remove a subnet from a router")
|
|
670
849
|
|
|
671
850
|
def get_parser(self, prog_name):
|
|
672
|
-
parser = super(
|
|
851
|
+
parser = super().get_parser(prog_name)
|
|
673
852
|
parser.add_argument(
|
|
674
853
|
'router',
|
|
675
854
|
metavar='<router>',
|
|
@@ -699,7 +878,7 @@ class SetRouter(common.NeutronCommandWithExtraArgs):
|
|
|
699
878
|
_description = _("Set router properties")
|
|
700
879
|
|
|
701
880
|
def get_parser(self, prog_name):
|
|
702
|
-
parser = super(
|
|
881
|
+
parser = super().get_parser(prog_name)
|
|
703
882
|
parser.add_argument(
|
|
704
883
|
'router',
|
|
705
884
|
metavar="<router>",
|
|
@@ -780,18 +959,25 @@ class SetRouter(common.NeutronCommandWithExtraArgs):
|
|
|
780
959
|
parser.add_argument(
|
|
781
960
|
'--external-gateway',
|
|
782
961
|
metavar="<network>",
|
|
783
|
-
|
|
962
|
+
action='append',
|
|
963
|
+
help=_(
|
|
964
|
+
"External Network used as router's gateway (name or ID). "
|
|
965
|
+
"(repeat option to set multiple gateways per router "
|
|
966
|
+
"if the L3 service plugin in use supports it)."
|
|
967
|
+
),
|
|
968
|
+
dest='external_gateways',
|
|
784
969
|
)
|
|
785
970
|
parser.add_argument(
|
|
786
971
|
'--fixed-ip',
|
|
787
972
|
metavar='subnet=<subnet>,ip-address=<ip-address>',
|
|
788
973
|
action=parseractions.MultiKeyValueAction,
|
|
789
974
|
optional_keys=['subnet', 'ip-address'],
|
|
975
|
+
dest='fixed_ips',
|
|
790
976
|
help=_(
|
|
791
977
|
"Desired IP and/or subnet (name or ID) "
|
|
792
978
|
"on external gateway: "
|
|
793
979
|
"subnet=<subnet>,ip-address=<ip-address> "
|
|
794
|
-
"(repeat option to set multiple fixed IP addresses)"
|
|
980
|
+
"(repeat option to set multiple fixed IP addresses)."
|
|
795
981
|
),
|
|
796
982
|
)
|
|
797
983
|
snat_group = parser.add_mutually_exclusive_group()
|
|
@@ -832,6 +1018,7 @@ class SetRouter(common.NeutronCommandWithExtraArgs):
|
|
|
832
1018
|
help=_("Remove QoS policy from router gateway IPs"),
|
|
833
1019
|
)
|
|
834
1020
|
_tag.add_tag_option_to_parser_for_set(parser, _('router'))
|
|
1021
|
+
_parser_add_bfd_ecmp_arguments(parser)
|
|
835
1022
|
return parser
|
|
836
1023
|
|
|
837
1024
|
def take_action(self, parsed_args):
|
|
@@ -860,8 +1047,8 @@ class SetRouter(common.NeutronCommandWithExtraArgs):
|
|
|
860
1047
|
if (
|
|
861
1048
|
parsed_args.disable_snat
|
|
862
1049
|
or parsed_args.enable_snat
|
|
863
|
-
or parsed_args.
|
|
864
|
-
) and not parsed_args.
|
|
1050
|
+
or parsed_args.fixed_ips
|
|
1051
|
+
) and not parsed_args.external_gateways:
|
|
865
1052
|
msg = _(
|
|
866
1053
|
"You must specify '--external-gateway' in order "
|
|
867
1054
|
"to update the SNAT or fixed-ip values"
|
|
@@ -870,7 +1057,7 @@ class SetRouter(common.NeutronCommandWithExtraArgs):
|
|
|
870
1057
|
|
|
871
1058
|
if (
|
|
872
1059
|
parsed_args.qos_policy or parsed_args.no_qos_policy
|
|
873
|
-
) and not parsed_args.
|
|
1060
|
+
) and not parsed_args.external_gateways:
|
|
874
1061
|
try:
|
|
875
1062
|
original_net_id = obj.external_gateway_info['network_id']
|
|
876
1063
|
except (KeyError, TypeError):
|
|
@@ -881,17 +1068,21 @@ class SetRouter(common.NeutronCommandWithExtraArgs):
|
|
|
881
1068
|
)
|
|
882
1069
|
raise exceptions.CommandError(msg)
|
|
883
1070
|
else:
|
|
884
|
-
if not attrs.get('external_gateway_info')
|
|
1071
|
+
if not attrs.get('external_gateway_info') and not attrs.get(
|
|
1072
|
+
'external_gateways'
|
|
1073
|
+
):
|
|
885
1074
|
attrs['external_gateway_info'] = {}
|
|
886
1075
|
attrs['external_gateway_info']['network_id'] = original_net_id
|
|
887
1076
|
if parsed_args.qos_policy:
|
|
888
1077
|
check_qos_id = client.find_qos_policy(
|
|
889
1078
|
parsed_args.qos_policy, ignore_missing=False
|
|
890
1079
|
).id
|
|
891
|
-
attrs
|
|
1080
|
+
if not attrs.get('external_gateways'):
|
|
1081
|
+
attrs['external_gateway_info']['qos_policy_id'] = check_qos_id
|
|
892
1082
|
|
|
893
1083
|
if 'no_qos_policy' in parsed_args and parsed_args.no_qos_policy:
|
|
894
|
-
attrs
|
|
1084
|
+
if not attrs.get('external_gateways'):
|
|
1085
|
+
attrs['external_gateway_info']['qos_policy_id'] = None
|
|
895
1086
|
|
|
896
1087
|
attrs.update(
|
|
897
1088
|
self._parse_extra_properties(parsed_args.extra_properties)
|
|
@@ -900,8 +1091,19 @@ class SetRouter(common.NeutronCommandWithExtraArgs):
|
|
|
900
1091
|
if parsed_args.enable_ndp_proxy is not None:
|
|
901
1092
|
attrs['enable_ndp_proxy'] = parsed_args.enable_ndp_proxy
|
|
902
1093
|
|
|
1094
|
+
_command_check_bfd_ecmp_supported(attrs, client)
|
|
1095
|
+
|
|
903
1096
|
if attrs:
|
|
1097
|
+
external_gateways = attrs.pop('external_gateways', None)
|
|
904
1098
|
client.update_router(obj, **attrs)
|
|
1099
|
+
# If the multiple external gateways API is intended to be used,
|
|
1100
|
+
# do a separate API call to set external gateways.
|
|
1101
|
+
if external_gateways:
|
|
1102
|
+
client.update_external_gateways(
|
|
1103
|
+
obj,
|
|
1104
|
+
body={'router': {'external_gateways': external_gateways}},
|
|
1105
|
+
)
|
|
1106
|
+
|
|
905
1107
|
# tags is a subresource and it needs to be updated separately.
|
|
906
1108
|
_tag.update_tags_for_set(client, obj, parsed_args)
|
|
907
1109
|
|
|
@@ -910,7 +1112,7 @@ class ShowRouter(command.ShowOne):
|
|
|
910
1112
|
_description = _("Display router details")
|
|
911
1113
|
|
|
912
1114
|
def get_parser(self, prog_name):
|
|
913
|
-
parser = super(
|
|
1115
|
+
parser = super().get_parser(prog_name)
|
|
914
1116
|
parser.add_argument(
|
|
915
1117
|
'router',
|
|
916
1118
|
metavar="<router>",
|
|
@@ -946,7 +1148,7 @@ class UnsetRouter(common.NeutronUnsetCommandWithExtraArgs):
|
|
|
946
1148
|
_description = _("Unset router properties")
|
|
947
1149
|
|
|
948
1150
|
def get_parser(self, prog_name):
|
|
949
|
-
parser = super(
|
|
1151
|
+
parser = super().get_parser(prog_name)
|
|
950
1152
|
parser.add_argument(
|
|
951
1153
|
'--route',
|
|
952
1154
|
metavar='destination=<subnet>,gateway=<ip-address>',
|
|
@@ -961,11 +1163,15 @@ class UnsetRouter(common.NeutronUnsetCommandWithExtraArgs):
|
|
|
961
1163
|
"(repeat option to unset multiple routes)"
|
|
962
1164
|
),
|
|
963
1165
|
)
|
|
1166
|
+
# NOTE(dmitriis): This was not extended to support selective removal
|
|
1167
|
+
# of external gateways due to a cpython bug in argparse:
|
|
1168
|
+
# https://github.com/python/cpython/issues/53584
|
|
964
1169
|
parser.add_argument(
|
|
965
1170
|
'--external-gateway',
|
|
966
1171
|
action='store_true',
|
|
967
1172
|
default=False,
|
|
968
1173
|
help=_("Remove external gateway information from the router"),
|
|
1174
|
+
dest='external_gateways',
|
|
969
1175
|
)
|
|
970
1176
|
parser.add_argument(
|
|
971
1177
|
'--qos-policy',
|
|
@@ -1012,7 +1218,7 @@ class UnsetRouter(common.NeutronUnsetCommandWithExtraArgs):
|
|
|
1012
1218
|
'qos_policy_id': None,
|
|
1013
1219
|
}
|
|
1014
1220
|
|
|
1015
|
-
if parsed_args.
|
|
1221
|
+
if parsed_args.external_gateways:
|
|
1016
1222
|
attrs['external_gateway_info'] = {}
|
|
1017
1223
|
|
|
1018
1224
|
attrs.update(
|
|
@@ -1020,6 +1226,149 @@ class UnsetRouter(common.NeutronUnsetCommandWithExtraArgs):
|
|
|
1020
1226
|
)
|
|
1021
1227
|
|
|
1022
1228
|
if attrs:
|
|
1229
|
+
# If removing multiple gateways per router are supported,
|
|
1230
|
+
# use the relevant API to remove them all.
|
|
1231
|
+
if is_multiple_gateways_supported(client):
|
|
1232
|
+
client.remove_external_gateways(
|
|
1233
|
+
obj,
|
|
1234
|
+
body={'router': {'external_gateways': {}}},
|
|
1235
|
+
)
|
|
1236
|
+
|
|
1023
1237
|
client.update_router(obj, **attrs)
|
|
1024
1238
|
# tags is a subresource and it needs to be updated separately.
|
|
1025
1239
|
_tag.update_tags_for_unset(client, obj, parsed_args)
|
|
1240
|
+
|
|
1241
|
+
|
|
1242
|
+
class AddGatewayToRouter(command.ShowOne):
|
|
1243
|
+
_description = _("Add router gateway")
|
|
1244
|
+
|
|
1245
|
+
def get_parser(self, prog_name):
|
|
1246
|
+
parser = super().get_parser(prog_name)
|
|
1247
|
+
parser.add_argument(
|
|
1248
|
+
'router',
|
|
1249
|
+
metavar="<router>",
|
|
1250
|
+
help=_("Router to modify (name or ID)."),
|
|
1251
|
+
)
|
|
1252
|
+
parser.add_argument(
|
|
1253
|
+
metavar="<network>",
|
|
1254
|
+
help=_(
|
|
1255
|
+
"External Network to a attach a router gateway to (name or "
|
|
1256
|
+
"ID)."
|
|
1257
|
+
),
|
|
1258
|
+
dest='external_gateways',
|
|
1259
|
+
# The argument is stored in a list in order to reuse the
|
|
1260
|
+
# common attribute parsing code.
|
|
1261
|
+
nargs=1,
|
|
1262
|
+
)
|
|
1263
|
+
parser.add_argument(
|
|
1264
|
+
'--fixed-ip',
|
|
1265
|
+
metavar='subnet=<subnet>,ip-address=<ip-address>',
|
|
1266
|
+
action=parseractions.MultiKeyValueAction,
|
|
1267
|
+
optional_keys=['subnet', 'ip-address'],
|
|
1268
|
+
dest='fixed_ips',
|
|
1269
|
+
help=_(
|
|
1270
|
+
"Desired IP and/or subnet (name or ID) "
|
|
1271
|
+
"on external gateway: "
|
|
1272
|
+
"subnet=<subnet>,ip-address=<ip-address> "
|
|
1273
|
+
"(repeat option to set multiple fixed IP addresses)."
|
|
1274
|
+
),
|
|
1275
|
+
)
|
|
1276
|
+
return parser
|
|
1277
|
+
|
|
1278
|
+
def take_action(self, parsed_args):
|
|
1279
|
+
client = self.app.client_manager.network
|
|
1280
|
+
if not is_multiple_gateways_supported(client):
|
|
1281
|
+
msg = _(
|
|
1282
|
+
'The external-gateway-multihoming extension is not enabled at '
|
|
1283
|
+
'the Neutron side.'
|
|
1284
|
+
)
|
|
1285
|
+
raise exceptions.CommandError(msg)
|
|
1286
|
+
|
|
1287
|
+
router_obj = client.find_router(
|
|
1288
|
+
parsed_args.router, ignore_missing=False
|
|
1289
|
+
)
|
|
1290
|
+
|
|
1291
|
+
# Get the common attributes.
|
|
1292
|
+
attrs = _get_external_gateway_attrs(
|
|
1293
|
+
self.app.client_manager, parsed_args
|
|
1294
|
+
)
|
|
1295
|
+
|
|
1296
|
+
if attrs:
|
|
1297
|
+
external_gateways = attrs.pop('external_gateways')
|
|
1298
|
+
router_obj = client.add_external_gateways(
|
|
1299
|
+
router_obj,
|
|
1300
|
+
body={'router': {'external_gateways': external_gateways}},
|
|
1301
|
+
)
|
|
1302
|
+
|
|
1303
|
+
display_columns, columns = _get_columns(router_obj)
|
|
1304
|
+
data = utils.get_item_properties(
|
|
1305
|
+
router_obj, columns, formatters=_formatters
|
|
1306
|
+
)
|
|
1307
|
+
return (display_columns, data)
|
|
1308
|
+
|
|
1309
|
+
|
|
1310
|
+
class RemoveGatewayFromRouter(command.ShowOne):
|
|
1311
|
+
_description = _("Remove router gateway")
|
|
1312
|
+
|
|
1313
|
+
def get_parser(self, prog_name):
|
|
1314
|
+
parser = super().get_parser(prog_name)
|
|
1315
|
+
parser.add_argument(
|
|
1316
|
+
'router',
|
|
1317
|
+
metavar="<router>",
|
|
1318
|
+
help=_("Router to modify (name or ID)."),
|
|
1319
|
+
)
|
|
1320
|
+
parser.add_argument(
|
|
1321
|
+
metavar="<network>",
|
|
1322
|
+
help=_(
|
|
1323
|
+
"External Network to remove a router gateway from (name or "
|
|
1324
|
+
"ID)."
|
|
1325
|
+
),
|
|
1326
|
+
dest='external_gateways',
|
|
1327
|
+
# The argument is stored in a list in order to reuse the
|
|
1328
|
+
# common attribute parsing code.
|
|
1329
|
+
nargs=1,
|
|
1330
|
+
)
|
|
1331
|
+
parser.add_argument(
|
|
1332
|
+
'--fixed-ip',
|
|
1333
|
+
metavar='subnet=<subnet>,ip-address=<ip-address>',
|
|
1334
|
+
action=parseractions.MultiKeyValueAction,
|
|
1335
|
+
optional_keys=['subnet', 'ip-address'],
|
|
1336
|
+
dest='fixed_ips',
|
|
1337
|
+
help=_(
|
|
1338
|
+
"IP and/or subnet (name or ID) on the external gateway "
|
|
1339
|
+
"which is used to identify a particular gateway if multiple "
|
|
1340
|
+
"are attached to the same network: subnet=<subnet>,"
|
|
1341
|
+
"ip-address=<ip-address>."
|
|
1342
|
+
),
|
|
1343
|
+
)
|
|
1344
|
+
return parser
|
|
1345
|
+
|
|
1346
|
+
def take_action(self, parsed_args):
|
|
1347
|
+
client = self.app.client_manager.network
|
|
1348
|
+
if not is_multiple_gateways_supported(client):
|
|
1349
|
+
msg = _(
|
|
1350
|
+
'The external-gateway-multihoming extension is not enabled at '
|
|
1351
|
+
'the Neutron side.'
|
|
1352
|
+
)
|
|
1353
|
+
raise exceptions.CommandError(msg)
|
|
1354
|
+
|
|
1355
|
+
router_obj = client.find_router(
|
|
1356
|
+
parsed_args.router, ignore_missing=False
|
|
1357
|
+
)
|
|
1358
|
+
|
|
1359
|
+
# Get the common attributes.
|
|
1360
|
+
attrs = _get_external_gateway_attrs(
|
|
1361
|
+
self.app.client_manager, parsed_args
|
|
1362
|
+
)
|
|
1363
|
+
if attrs:
|
|
1364
|
+
external_gateways = attrs.pop('external_gateways')
|
|
1365
|
+
router_obj = client.remove_external_gateways(
|
|
1366
|
+
router_obj,
|
|
1367
|
+
body={'router': {'external_gateways': external_gateways}},
|
|
1368
|
+
)
|
|
1369
|
+
|
|
1370
|
+
display_columns, columns = _get_columns(router_obj)
|
|
1371
|
+
data = utils.get_item_properties(
|
|
1372
|
+
router_obj, columns, formatters=_formatters
|
|
1373
|
+
)
|
|
1374
|
+
return (display_columns, data)
|