api_replay for migration into the policy plugin
- Add api reply support in the policy plugin - Move some api_replay common code to the common_v3 plugin - Add support for avaiablity zones in the api_replay Change-Id: Idb376e2d8c0b24f2fea5f051af2191f77831803c
This commit is contained in:
parent
5216a35ffd
commit
905310e2fc
@ -35,6 +35,7 @@ class ApiReplayCli(object):
|
|||||||
dest_os_user_domain_id=args.dest_os_user_domain_id,
|
dest_os_user_domain_id=args.dest_os_user_domain_id,
|
||||||
dest_os_password=args.dest_os_password,
|
dest_os_password=args.dest_os_password,
|
||||||
dest_os_auth_url=args.dest_os_auth_url,
|
dest_os_auth_url=args.dest_os_auth_url,
|
||||||
|
dest_plugin=args.dest_plugin,
|
||||||
use_old_keystone=args.use_old_keystone,
|
use_old_keystone=args.use_old_keystone,
|
||||||
logfile=args.logfile)
|
logfile=args.logfile)
|
||||||
|
|
||||||
@ -101,7 +102,11 @@ class ApiReplayCli(object):
|
|||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
"--dest-os-auth-url",
|
"--dest-os-auth-url",
|
||||||
required=True,
|
required=True,
|
||||||
help="They keystone api endpoint for this user.")
|
help="The keystone api endpoint for this user.")
|
||||||
|
parser.add_argument(
|
||||||
|
"--dest-plugin",
|
||||||
|
default='nsx-p',
|
||||||
|
help="The core plugin of the destination nsx-t/nsx-p.")
|
||||||
|
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
"--use-old-keystone",
|
"--use-old-keystone",
|
||||||
|
@ -38,7 +38,7 @@ class ApiReplayClient(utils.PrepareObjectForMigration):
|
|||||||
source_os_password, source_os_auth_url,
|
source_os_password, source_os_auth_url,
|
||||||
dest_os_username, dest_os_user_domain_id,
|
dest_os_username, dest_os_user_domain_id,
|
||||||
dest_os_tenant_name, dest_os_tenant_domain_id,
|
dest_os_tenant_name, dest_os_tenant_domain_id,
|
||||||
dest_os_password, dest_os_auth_url,
|
dest_os_password, dest_os_auth_url, dest_plugin,
|
||||||
use_old_keystone, logfile):
|
use_old_keystone, logfile):
|
||||||
|
|
||||||
if logfile:
|
if logfile:
|
||||||
@ -80,8 +80,9 @@ class ApiReplayClient(utils.PrepareObjectForMigration):
|
|||||||
tenant_domain_id=dest_os_tenant_domain_id,
|
tenant_domain_id=dest_os_tenant_domain_id,
|
||||||
password=dest_os_password,
|
password=dest_os_password,
|
||||||
auth_url=dest_os_auth_url)
|
auth_url=dest_os_auth_url)
|
||||||
|
self.dest_plugin = dest_plugin
|
||||||
|
|
||||||
LOG.info("Starting NSX migration.")
|
LOG.info("Starting NSX migration to %s.", self.dest_plugin)
|
||||||
# Migrate all the objects
|
# Migrate all the objects
|
||||||
self.migrate_security_groups()
|
self.migrate_security_groups()
|
||||||
self.migrate_qos_policies()
|
self.migrate_qos_policies()
|
||||||
@ -269,6 +270,11 @@ class ApiReplayClient(utils.PrepareObjectForMigration):
|
|||||||
# is raised here but that's okay.
|
# is raised here but that's okay.
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
def get_dest_availablity_zones(self, resource):
|
||||||
|
azs = self.dest_neutron.list_availability_zones()['availability_zones']
|
||||||
|
az_names = [az['name'] for az in azs if az['resource'] == resource]
|
||||||
|
return az_names
|
||||||
|
|
||||||
def migrate_routers(self):
|
def migrate_routers(self):
|
||||||
"""Migrates routers from source to dest neutron.
|
"""Migrates routers from source to dest neutron.
|
||||||
|
|
||||||
@ -284,6 +290,7 @@ class ApiReplayClient(utils.PrepareObjectForMigration):
|
|||||||
source_routers = []
|
source_routers = []
|
||||||
|
|
||||||
dest_routers = self.dest_neutron.list_routers()['routers']
|
dest_routers = self.dest_neutron.list_routers()['routers']
|
||||||
|
dest_azs = self.get_dest_availablity_zones('router')
|
||||||
update_routes = {}
|
update_routes = {}
|
||||||
gw_info = {}
|
gw_info = {}
|
||||||
|
|
||||||
@ -304,7 +311,7 @@ class ApiReplayClient(utils.PrepareObjectForMigration):
|
|||||||
|
|
||||||
dest_router = self.have_id(router['id'], dest_routers)
|
dest_router = self.have_id(router['id'], dest_routers)
|
||||||
if dest_router is False:
|
if dest_router is False:
|
||||||
body = self.prepare_router(router)
|
body = self.prepare_router(router, dest_azs=dest_azs)
|
||||||
try:
|
try:
|
||||||
new_router = (self.dest_neutron.create_router(
|
new_router = (self.dest_neutron.create_router(
|
||||||
{'router': body}))
|
{'router': body}))
|
||||||
@ -390,6 +397,7 @@ class ApiReplayClient(utils.PrepareObjectForMigration):
|
|||||||
dest_default_public_net = True
|
dest_default_public_net = True
|
||||||
|
|
||||||
subnetpools_map = self.migrate_subnetpools()
|
subnetpools_map = self.migrate_subnetpools()
|
||||||
|
dest_azs = self.get_dest_availablity_zones('network')
|
||||||
|
|
||||||
total_num = len(source_networks)
|
total_num = len(source_networks)
|
||||||
LOG.info("Migrating %(nets)s networks, %(subnets)s subnets and "
|
LOG.info("Migrating %(nets)s networks, %(subnets)s subnets and "
|
||||||
@ -400,7 +408,8 @@ class ApiReplayClient(utils.PrepareObjectForMigration):
|
|||||||
external_net = network.get('router:external')
|
external_net = network.get('router:external')
|
||||||
body = self.prepare_network(
|
body = self.prepare_network(
|
||||||
network, remove_qos=remove_qos,
|
network, remove_qos=remove_qos,
|
||||||
dest_default_public_net=dest_default_public_net)
|
dest_default_public_net=dest_default_public_net,
|
||||||
|
dest_azs=dest_azs)
|
||||||
|
|
||||||
# only create network if the dest server doesn't have it
|
# only create network if the dest server doesn't have it
|
||||||
if self.have_id(network['id'], dest_networks):
|
if self.have_id(network['id'], dest_networks):
|
||||||
@ -450,7 +459,7 @@ class ApiReplayClient(utils.PrepareObjectForMigration):
|
|||||||
body['enable_dhcp'] = False
|
body['enable_dhcp'] = False
|
||||||
if count_dhcp_subnet > 1:
|
if count_dhcp_subnet > 1:
|
||||||
# Do not allow dhcp on the subnet if there is already
|
# Do not allow dhcp on the subnet if there is already
|
||||||
# another subnet with DHCP as the v3 plugin supports
|
# another subnet with DHCP as the v3 plugins supports
|
||||||
# only one
|
# only one
|
||||||
LOG.warning("Disabling DHCP for subnet on net %s: "
|
LOG.warning("Disabling DHCP for subnet on net %s: "
|
||||||
"The plugin doesn't support multiple "
|
"The plugin doesn't support multiple "
|
||||||
@ -561,7 +570,7 @@ class ApiReplayClient(utils.PrepareObjectForMigration):
|
|||||||
# script multiple times as we don't track this.
|
# script multiple times as we don't track this.
|
||||||
# Note(asarfaty): also if the same network in
|
# Note(asarfaty): also if the same network in
|
||||||
# source is attached to 2 routers, which the v3
|
# source is attached to 2 routers, which the v3
|
||||||
# plugin does not support.
|
# plugins does not support.
|
||||||
LOG.error("Failed to add router interface port"
|
LOG.error("Failed to add router interface port"
|
||||||
"(%(port)s): %(e)s",
|
"(%(port)s): %(e)s",
|
||||||
{'port': port, 'e': e})
|
{'port': port, 'e': e})
|
||||||
|
@ -46,10 +46,10 @@ def _fixup_res_dict(context, attr_name, res_dict, check_allow_post=True):
|
|||||||
|
|
||||||
|
|
||||||
class PrepareObjectForMigration(object):
|
class PrepareObjectForMigration(object):
|
||||||
"""Helper class to modify V objects before creating them in T"""
|
"""Helper class to modify source objects before creating them in dest"""
|
||||||
# Remove some fields before creating the new object.
|
# Remove some fields before creating the new object.
|
||||||
# Some fields are not supported for a new object, and some are not
|
# Some fields are not supported for a new object, and some are not
|
||||||
# supported by the nsx-v3 plugin
|
# supported by the destination plugin
|
||||||
basic_ignore_fields = ['updated_at',
|
basic_ignore_fields = ['updated_at',
|
||||||
'created_at',
|
'created_at',
|
||||||
'tags',
|
'tags',
|
||||||
@ -64,7 +64,6 @@ class PrepareObjectForMigration(object):
|
|||||||
'ha',
|
'ha',
|
||||||
'external_gateway_info',
|
'external_gateway_info',
|
||||||
'router_type',
|
'router_type',
|
||||||
'availability_zone_hints',
|
|
||||||
'availability_zones',
|
'availability_zones',
|
||||||
'distributed',
|
'distributed',
|
||||||
'flavor_id']
|
'flavor_id']
|
||||||
@ -89,7 +88,6 @@ class PrepareObjectForMigration(object):
|
|||||||
'status',
|
'status',
|
||||||
'subnets',
|
'subnets',
|
||||||
'availability_zones',
|
'availability_zones',
|
||||||
'availability_zone_hints',
|
|
||||||
'ipv4_address_scope',
|
'ipv4_address_scope',
|
||||||
'ipv6_address_scope',
|
'ipv6_address_scope',
|
||||||
'mtu']
|
'mtu']
|
||||||
@ -124,10 +122,18 @@ class PrepareObjectForMigration(object):
|
|||||||
self.fix_description(sg)
|
self.fix_description(sg)
|
||||||
return self.drop_fields(sg, self.drop_sg_fields)
|
return self.drop_fields(sg, self.drop_sg_fields)
|
||||||
|
|
||||||
def prepare_router(self, rtr, direct_call=False):
|
def prepare_router(self, rtr, dest_azs=None, direct_call=False):
|
||||||
self.fix_description(rtr)
|
self.fix_description(rtr)
|
||||||
body = self.drop_fields(rtr, self.drop_router_fields)
|
body = self.drop_fields(rtr, self.drop_router_fields)
|
||||||
if direct_call:
|
if dest_azs:
|
||||||
|
if body.get('availability_zone_hints'):
|
||||||
|
az = body['availability_zone_hints'][0]
|
||||||
|
if az not in dest_azs:
|
||||||
|
if az != 'default':
|
||||||
|
LOG.warning("Ignoring AZ %s in router %s as it is not "
|
||||||
|
"defined in destination", az, rtr['id'])
|
||||||
|
body['availability_zone_hints'] = []
|
||||||
|
elif direct_call:
|
||||||
body['availability_zone_hints'] = []
|
body['availability_zone_hints'] = []
|
||||||
return body
|
return body
|
||||||
|
|
||||||
@ -136,7 +142,7 @@ class PrepareObjectForMigration(object):
|
|||||||
return self.drop_fields(pool, self.drop_subnetpool_fields)
|
return self.drop_fields(pool, self.drop_subnetpool_fields)
|
||||||
|
|
||||||
def prepare_network(self, net, dest_default_public_net=True,
|
def prepare_network(self, net, dest_default_public_net=True,
|
||||||
remove_qos=False, direct_call=False):
|
remove_qos=False, dest_azs=None, direct_call=False):
|
||||||
self.fix_description(net)
|
self.fix_description(net)
|
||||||
body = self.drop_fields(net, self.drop_network_fields)
|
body = self.drop_fields(net, self.drop_network_fields)
|
||||||
|
|
||||||
@ -151,13 +157,13 @@ class PrepareObjectForMigration(object):
|
|||||||
del body[field]
|
del body[field]
|
||||||
|
|
||||||
# vxlan network with segmentation id should be translated to a regular
|
# vxlan network with segmentation id should be translated to a regular
|
||||||
# network in nsx-v3.
|
# network in nsx-v3/P.
|
||||||
if (body.get('provider:network_type') == 'vxlan' and
|
if (body.get('provider:network_type') == 'vxlan' and
|
||||||
body.get('provider:segmentation_id') is not None):
|
body.get('provider:segmentation_id') is not None):
|
||||||
del body['provider:network_type']
|
del body['provider:network_type']
|
||||||
del body['provider:segmentation_id']
|
del body['provider:segmentation_id']
|
||||||
|
|
||||||
# flat network should be translated to a regular network in nsx-v3.
|
# flat network should be translated to a regular network in nsx-v3/P.
|
||||||
if (body.get('provider:network_type') == 'flat'):
|
if (body.get('provider:network_type') == 'flat'):
|
||||||
del body['provider:network_type']
|
del body['provider:network_type']
|
||||||
if 'provider:physical_network' in body:
|
if 'provider:physical_network' in body:
|
||||||
@ -179,7 +185,15 @@ class PrepareObjectForMigration(object):
|
|||||||
body['is_default'] = False
|
body['is_default'] = False
|
||||||
LOG.warning("Public network %s was set to non default network",
|
LOG.warning("Public network %s was set to non default network",
|
||||||
body['id'])
|
body['id'])
|
||||||
if direct_call:
|
if dest_azs:
|
||||||
|
if body.get('availability_zone_hints'):
|
||||||
|
az = body['availability_zone_hints'][0]
|
||||||
|
if az not in dest_azs:
|
||||||
|
if az != 'default':
|
||||||
|
LOG.warning("Ignoring AZ %s in net %s as it is not "
|
||||||
|
"defined in destination", az, body['id'])
|
||||||
|
body['availability_zone_hints'] = []
|
||||||
|
elif direct_call:
|
||||||
body['availability_zone_hints'] = []
|
body['availability_zone_hints'] = []
|
||||||
return body
|
return body
|
||||||
|
|
||||||
|
@ -246,7 +246,7 @@ nsx_common_opts = [
|
|||||||
help=_("If true, the server then allows the caller to "
|
help=_("If true, the server then allows the caller to "
|
||||||
"specify the id of resources. This should only "
|
"specify the id of resources. This should only "
|
||||||
"be enabled in order to allow one to migrate an "
|
"be enabled in order to allow one to migrate an "
|
||||||
"existing install of neutron to the NSX-T plugin.")),
|
"existing install of neutron to a new VMWare plugin.")),
|
||||||
cfg.ListOpt('nsx_extension_drivers',
|
cfg.ListOpt('nsx_extension_drivers',
|
||||||
default=[],
|
default=[],
|
||||||
help=_("An ordered list of extension driver "
|
help=_("An ordered list of extension driver "
|
||||||
|
@ -13,7 +13,8 @@
|
|||||||
# License for the specific language governing permissions and limitations
|
# License for the specific language governing permissions and limitations
|
||||||
# under the License.
|
# under the License.
|
||||||
|
|
||||||
|
import decorator
|
||||||
|
import mock
|
||||||
import netaddr
|
import netaddr
|
||||||
from oslo_config import cfg
|
from oslo_config import cfg
|
||||||
from oslo_db import exception as db_exc
|
from oslo_db import exception as db_exc
|
||||||
@ -38,6 +39,7 @@ from neutron.db import extraroute_db
|
|||||||
from neutron.db import l3_attrs_db
|
from neutron.db import l3_attrs_db
|
||||||
from neutron.db import l3_db
|
from neutron.db import l3_db
|
||||||
from neutron.db import l3_gwmode_db
|
from neutron.db import l3_gwmode_db
|
||||||
|
from neutron.db.models import securitygroup as securitygroup_model
|
||||||
from neutron.db import models_v2
|
from neutron.db import models_v2
|
||||||
from neutron.db import portbindings_db
|
from neutron.db import portbindings_db
|
||||||
from neutron.db import portsecurity_db
|
from neutron.db import portsecurity_db
|
||||||
@ -70,6 +72,7 @@ from neutron_lib.services.qos import constants as qos_consts
|
|||||||
from neutron_lib.utils import helpers
|
from neutron_lib.utils import helpers
|
||||||
from neutron_lib.utils import net as nl_net_utils
|
from neutron_lib.utils import net as nl_net_utils
|
||||||
|
|
||||||
|
from vmware_nsx.api_replay import utils as api_replay_utils
|
||||||
from vmware_nsx.common import availability_zones as nsx_com_az
|
from vmware_nsx.common import availability_zones as nsx_com_az
|
||||||
from vmware_nsx.common import exceptions as nsx_exc
|
from vmware_nsx.common import exceptions as nsx_exc
|
||||||
from vmware_nsx.common import locking
|
from vmware_nsx.common import locking
|
||||||
@ -95,6 +98,17 @@ from vmware_nsxlib.v3 import utils as nsxlib_utils
|
|||||||
LOG = logging.getLogger(__name__)
|
LOG = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
@decorator.decorator
|
||||||
|
def api_replay_mode_wrapper(f, *args, **kwargs):
|
||||||
|
if cfg.CONF.api_replay_mode:
|
||||||
|
# NOTE(arosen): the mock.patch here is needed for api_replay_mode
|
||||||
|
with mock.patch("neutron_lib.plugins.utils._fixup_res_dict",
|
||||||
|
side_effect=api_replay_utils._fixup_res_dict):
|
||||||
|
return f(*args, **kwargs)
|
||||||
|
else:
|
||||||
|
return f(*args, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
# NOTE(asarfaty): the order of inheritance here is important. in order for the
|
# NOTE(asarfaty): the order of inheritance here is important. in order for the
|
||||||
# QoS notification to work, the AgentScheduler init must be called first
|
# QoS notification to work, the AgentScheduler init must be called first
|
||||||
# NOTE(arosen): same is true with the ExtendedSecurityGroupPropertiesMixin
|
# NOTE(arosen): same is true with the ExtendedSecurityGroupPropertiesMixin
|
||||||
@ -2761,6 +2775,38 @@ class NsxPluginV3Base(agentschedulers_db.AZDhcpAgentSchedulerDbMixin,
|
|||||||
|
|
||||||
return restricted_vlans
|
return restricted_vlans
|
||||||
|
|
||||||
|
@api_replay_mode_wrapper
|
||||||
|
def _create_floating_ip_wrapper(self, context, floatingip):
|
||||||
|
initial_status = (constants.FLOATINGIP_STATUS_ACTIVE
|
||||||
|
if floatingip['floatingip']['port_id']
|
||||||
|
else constants.FLOATINGIP_STATUS_DOWN)
|
||||||
|
return super(NsxPluginV3Base, self).create_floatingip(
|
||||||
|
context, floatingip, initial_status=initial_status)
|
||||||
|
|
||||||
|
def _ensure_default_security_group(self, context, tenant_id):
|
||||||
|
# NOTE(arosen): if in replay mode we'll create all the default
|
||||||
|
# security groups for the user with their data so we don't
|
||||||
|
# want this to be called.
|
||||||
|
if not cfg.CONF.api_replay_mode:
|
||||||
|
return super(NsxPluginV3Base, self)._ensure_default_security_group(
|
||||||
|
context, tenant_id)
|
||||||
|
|
||||||
|
def _handle_api_replay_default_sg(self, context, secgroup_db):
|
||||||
|
"""Set default api-replay migrated SG as default manually"""
|
||||||
|
if (secgroup_db['name'] == 'default'):
|
||||||
|
# this is a default security group copied from another cloud
|
||||||
|
# Ugly patch! mark it as default manually
|
||||||
|
with context.session.begin(subtransactions=True):
|
||||||
|
try:
|
||||||
|
default_entry = securitygroup_model.DefaultSecurityGroup(
|
||||||
|
security_group_id=secgroup_db['id'],
|
||||||
|
project_id=secgroup_db['project_id'])
|
||||||
|
context.session.add(default_entry)
|
||||||
|
except Exception as e:
|
||||||
|
LOG.error("Failed to mark migrated security group %(id)s "
|
||||||
|
"as default %(e)s",
|
||||||
|
{'id': secgroup_db['id'], 'e': e})
|
||||||
|
|
||||||
|
|
||||||
class TagsCallbacks(object):
|
class TagsCallbacks(object):
|
||||||
|
|
||||||
|
@ -68,6 +68,7 @@ from vmware_nsx.common import locking
|
|||||||
from vmware_nsx.common import managers
|
from vmware_nsx.common import managers
|
||||||
from vmware_nsx.common import utils
|
from vmware_nsx.common import utils
|
||||||
from vmware_nsx.db import db as nsx_db
|
from vmware_nsx.db import db as nsx_db
|
||||||
|
from vmware_nsx.extensions import api_replay
|
||||||
from vmware_nsx.extensions import maclearning as mac_ext
|
from vmware_nsx.extensions import maclearning as mac_ext
|
||||||
from vmware_nsx.extensions import projectpluginmap
|
from vmware_nsx.extensions import projectpluginmap
|
||||||
from vmware_nsx.extensions import providersecuritygroup as provider_sg
|
from vmware_nsx.extensions import providersecuritygroup as provider_sg
|
||||||
@ -214,6 +215,10 @@ class NsxPolicyPlugin(nsx_plugin_common.NsxPluginV3Base):
|
|||||||
if cfg.CONF.vlan_transparent:
|
if cfg.CONF.vlan_transparent:
|
||||||
self.supported_extension_aliases.append(vlan_apidef.ALIAS)
|
self.supported_extension_aliases.append(vlan_apidef.ALIAS)
|
||||||
|
|
||||||
|
# Support api-reply for migration environments to the policy plugin
|
||||||
|
if cfg.CONF.api_replay_mode:
|
||||||
|
self.supported_extension_aliases.append(api_replay.ALIAS)
|
||||||
|
|
||||||
nsxlib_utils.set_inject_headers_callback(v3_utils.inject_headers)
|
nsxlib_utils.set_inject_headers_callback(v3_utils.inject_headers)
|
||||||
self._validate_nsx_policy_version()
|
self._validate_nsx_policy_version()
|
||||||
self._validate_config()
|
self._validate_config()
|
||||||
@ -1787,6 +1792,7 @@ class NsxPolicyPlugin(nsx_plugin_common.NsxPluginV3Base):
|
|||||||
router_id,
|
router_id,
|
||||||
static_route_id=self._get_static_route_id(route))
|
static_route_id=self._get_static_route_id(route))
|
||||||
|
|
||||||
|
@nsx_plugin_common.api_replay_mode_wrapper
|
||||||
def update_router(self, context, router_id, router):
|
def update_router(self, context, router_id, router):
|
||||||
gw_info = self._extract_external_gw(context, router, is_extract=False)
|
gw_info = self._extract_external_gw(context, router, is_extract=False)
|
||||||
router_data = router['router']
|
router_data = router['router']
|
||||||
@ -1857,6 +1863,7 @@ class NsxPolicyPlugin(nsx_plugin_common.NsxPluginV3Base):
|
|||||||
cidr_prefix = int(subnet['cidr'].split('/')[1])
|
cidr_prefix = int(subnet['cidr'].split('/')[1])
|
||||||
return "%s/%s" % (subnet['gateway_ip'], cidr_prefix)
|
return "%s/%s" % (subnet['gateway_ip'], cidr_prefix)
|
||||||
|
|
||||||
|
@nsx_plugin_common.api_replay_mode_wrapper
|
||||||
def add_router_interface(self, context, router_id, interface_info):
|
def add_router_interface(self, context, router_id, interface_info):
|
||||||
network_id = self._get_interface_network(context, interface_info)
|
network_id = self._get_interface_network(context, interface_info)
|
||||||
extern_net = self._network_is_external(context, network_id)
|
extern_net = self._network_is_external(context, network_id)
|
||||||
@ -2131,10 +2138,7 @@ class NsxPolicyPlugin(nsx_plugin_common.NsxPluginV3Base):
|
|||||||
self._assert_on_assoc_floatingip_to_special_ports(
|
self._assert_on_assoc_floatingip_to_special_ports(
|
||||||
fip_data, port_data)
|
fip_data, port_data)
|
||||||
|
|
||||||
new_fip = super(NsxPolicyPlugin, self).create_floatingip(
|
new_fip = self._create_floating_ip_wrapper(context, floatingip)
|
||||||
context, floatingip, initial_status=(
|
|
||||||
const.FLOATINGIP_STATUS_ACTIVE
|
|
||||||
if port_id else const.FLOATINGIP_STATUS_DOWN))
|
|
||||||
router_id = new_fip['router_id']
|
router_id = new_fip['router_id']
|
||||||
if not router_id:
|
if not router_id:
|
||||||
return new_fip
|
return new_fip
|
||||||
@ -2639,6 +2643,9 @@ class NsxPolicyPlugin(nsx_plugin_common.NsxPluginV3Base):
|
|||||||
secgroup,
|
secgroup,
|
||||||
default_sg)
|
default_sg)
|
||||||
|
|
||||||
|
if cfg.CONF.api_replay_mode:
|
||||||
|
self._handle_api_replay_default_sg(context, secgroup_db)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
# Create Group & communication map on the NSX
|
# Create Group & communication map on the NSX
|
||||||
self._create_security_group_backend_resources(
|
self._create_security_group_backend_resources(
|
||||||
|
@ -69,7 +69,6 @@ from oslo_utils import importutils
|
|||||||
from oslo_utils import uuidutils
|
from oslo_utils import uuidutils
|
||||||
|
|
||||||
from vmware_nsx._i18n import _
|
from vmware_nsx._i18n import _
|
||||||
from vmware_nsx.api_replay import utils as api_replay_utils
|
|
||||||
from vmware_nsx.common import config # noqa
|
from vmware_nsx.common import config # noqa
|
||||||
from vmware_nsx.common import exceptions as nsx_exc
|
from vmware_nsx.common import exceptions as nsx_exc
|
||||||
from vmware_nsx.common import l3_rpc_agent_api
|
from vmware_nsx.common import l3_rpc_agent_api
|
||||||
@ -2383,17 +2382,7 @@ class NsxV3Plugin(nsx_plugin_common.NsxPluginV3Base,
|
|||||||
|
|
||||||
return ret_val
|
return ret_val
|
||||||
|
|
||||||
def _update_router_wrapper(self, context, router_id, router):
|
@nsx_plugin_common.api_replay_mode_wrapper
|
||||||
if cfg.CONF.api_replay_mode:
|
|
||||||
# NOTE(arosen): the mock.patch here is needed for api_replay_mode
|
|
||||||
with mock.patch("neutron_lib.plugins.utils._fixup_res_dict",
|
|
||||||
side_effect=api_replay_utils._fixup_res_dict):
|
|
||||||
return super(NsxV3Plugin, self).update_router(
|
|
||||||
context, router_id, router)
|
|
||||||
else:
|
|
||||||
return super(NsxV3Plugin, self).update_router(
|
|
||||||
context, router_id, router)
|
|
||||||
|
|
||||||
def update_router(self, context, router_id, router):
|
def update_router(self, context, router_id, router):
|
||||||
gw_info = self._extract_external_gw(context, router, is_extract=False)
|
gw_info = self._extract_external_gw(context, router, is_extract=False)
|
||||||
router_data = router['router']
|
router_data = router['router']
|
||||||
@ -2473,7 +2462,8 @@ class NsxV3Plugin(nsx_plugin_common.NsxPluginV3Base,
|
|||||||
nsx_router_id,
|
nsx_router_id,
|
||||||
description=router_data['description'])
|
description=router_data['description'])
|
||||||
|
|
||||||
return self._update_router_wrapper(context, router_id, router)
|
return super(NsxV3Plugin, self).update_router(
|
||||||
|
context, router_id, router)
|
||||||
except nsx_lib_exc.ResourceNotFound:
|
except nsx_lib_exc.ResourceNotFound:
|
||||||
with db_api.CONTEXT_WRITER.using(context):
|
with db_api.CONTEXT_WRITER.using(context):
|
||||||
router_db = self._get_router(context, router_id)
|
router_db = self._get_router(context, router_id)
|
||||||
@ -2670,18 +2660,7 @@ class NsxV3Plugin(nsx_plugin_common.NsxPluginV3Base,
|
|||||||
|
|
||||||
return (ports, address_groups)
|
return (ports, address_groups)
|
||||||
|
|
||||||
def _add_router_interface_wrapper(self, context, router_id,
|
@nsx_plugin_common.api_replay_mode_wrapper
|
||||||
interface_info):
|
|
||||||
if cfg.CONF.api_replay_mode:
|
|
||||||
# NOTE(arosen): the mock.patch here is needed for api_replay_mode
|
|
||||||
with mock.patch("neutron_lib.plugins.utils._fixup_res_dict",
|
|
||||||
side_effect=api_replay_utils._fixup_res_dict):
|
|
||||||
return super(NsxV3Plugin, self).add_router_interface(
|
|
||||||
context, router_id, interface_info)
|
|
||||||
else:
|
|
||||||
return super(NsxV3Plugin, self).add_router_interface(
|
|
||||||
context, router_id, interface_info)
|
|
||||||
|
|
||||||
def add_router_interface(self, context, router_id, interface_info):
|
def add_router_interface(self, context, router_id, interface_info):
|
||||||
network_id = self._get_interface_network(context, interface_info)
|
network_id = self._get_interface_network(context, interface_info)
|
||||||
extern_net = self._network_is_external(context, network_id)
|
extern_net = self._network_is_external(context, network_id)
|
||||||
@ -2717,8 +2696,8 @@ class NsxV3Plugin(nsx_plugin_common.NsxPluginV3Base,
|
|||||||
[network_id])
|
[network_id])
|
||||||
|
|
||||||
# Update the interface of the neutron router
|
# Update the interface of the neutron router
|
||||||
info = self._add_router_interface_wrapper(context, router_id,
|
info = super(NsxV3Plugin, self).add_router_interface(
|
||||||
interface_info)
|
context, router_id, interface_info)
|
||||||
try:
|
try:
|
||||||
subnet = self.get_subnet(context, info['subnet_ids'][0])
|
subnet = self.get_subnet(context, info['subnet_ids'][0])
|
||||||
port = self.get_port(context, info['port_id'])
|
port = self.get_port(context, info['port_id'])
|
||||||
@ -2919,23 +2898,6 @@ class NsxV3Plugin(nsx_plugin_common.NsxPluginV3Base,
|
|||||||
vs_client.update_virtual_server_with_vip(vs['id'],
|
vs_client.update_virtual_server_with_vip(vs['id'],
|
||||||
vip_address)
|
vip_address)
|
||||||
|
|
||||||
def _create_floating_ip_wrapper(self, context, floatingip):
|
|
||||||
if cfg.CONF.api_replay_mode:
|
|
||||||
# NOTE(arosen): the mock.patch here is needed for api_replay_mode
|
|
||||||
with mock.patch("neutron_lib.plugins.utils._fixup_res_dict",
|
|
||||||
side_effect=api_replay_utils._fixup_res_dict):
|
|
||||||
return super(NsxV3Plugin, self).create_floatingip(
|
|
||||||
context, floatingip, initial_status=(
|
|
||||||
const.FLOATINGIP_STATUS_ACTIVE
|
|
||||||
if floatingip['floatingip']['port_id']
|
|
||||||
else const.FLOATINGIP_STATUS_DOWN))
|
|
||||||
else:
|
|
||||||
return super(NsxV3Plugin, self).create_floatingip(
|
|
||||||
context, floatingip, initial_status=(
|
|
||||||
const.FLOATINGIP_STATUS_ACTIVE
|
|
||||||
if floatingip['floatingip']['port_id']
|
|
||||||
else const.FLOATINGIP_STATUS_DOWN))
|
|
||||||
|
|
||||||
def create_floatingip(self, context, floatingip):
|
def create_floatingip(self, context, floatingip):
|
||||||
# First do some validations
|
# First do some validations
|
||||||
fip_data = floatingip['floatingip']
|
fip_data = floatingip['floatingip']
|
||||||
@ -3122,14 +3084,6 @@ class NsxV3Plugin(nsx_plugin_common.NsxPluginV3Base,
|
|||||||
super(NsxV3Plugin, self).disassociate_floatingips(
|
super(NsxV3Plugin, self).disassociate_floatingips(
|
||||||
context, port_id, do_notify=False)
|
context, port_id, do_notify=False)
|
||||||
|
|
||||||
def _ensure_default_security_group(self, context, tenant_id):
|
|
||||||
# NOTE(arosen): if in replay mode we'll create all the default
|
|
||||||
# security groups for the user with their data so we don't
|
|
||||||
# want this to be called.
|
|
||||||
if (cfg.CONF.api_replay_mode is False):
|
|
||||||
return super(NsxV3Plugin, self)._ensure_default_security_group(
|
|
||||||
context, tenant_id)
|
|
||||||
|
|
||||||
def _create_fw_section_for_secgroup(self, nsgroup, is_provider):
|
def _create_fw_section_for_secgroup(self, nsgroup, is_provider):
|
||||||
# NOTE(arosen): if a security group is provider we want to
|
# NOTE(arosen): if a security group is provider we want to
|
||||||
# insert our rules at the top.
|
# insert our rules at the top.
|
||||||
@ -3190,22 +3144,6 @@ class NsxV3Plugin(nsx_plugin_common.NsxPluginV3Base,
|
|||||||
logging_enabled, action, _sg_rules,
|
logging_enabled, action, _sg_rules,
|
||||||
ruleid_2_remote_nsgroup_map)
|
ruleid_2_remote_nsgroup_map)
|
||||||
|
|
||||||
def _handle_api_replay_default_sg(self, context, secgroup_db):
|
|
||||||
"""Set default api-replay migrated SG as default manually"""
|
|
||||||
if (secgroup_db['name'] == 'default'):
|
|
||||||
# this is a default security group copied from another cloud
|
|
||||||
# Ugly patch! mark it as default manually
|
|
||||||
with context.session.begin(subtransactions=True):
|
|
||||||
try:
|
|
||||||
default_entry = securitygroup_model.DefaultSecurityGroup(
|
|
||||||
security_group_id=secgroup_db['id'],
|
|
||||||
project_id=secgroup_db['project_id'])
|
|
||||||
context.session.add(default_entry)
|
|
||||||
except Exception as e:
|
|
||||||
LOG.error("Failed to mark migrated security group %(id)s "
|
|
||||||
"as default %(e)s",
|
|
||||||
{'id': secgroup_db['id'], 'e': e})
|
|
||||||
|
|
||||||
def create_security_group(self, context, security_group, default_sg=False):
|
def create_security_group(self, context, security_group, default_sg=False):
|
||||||
secgroup = security_group['security_group']
|
secgroup = security_group['security_group']
|
||||||
secgroup['id'] = secgroup.get('id') or uuidutils.generate_uuid()
|
secgroup['id'] = secgroup.get('id') or uuidutils.generate_uuid()
|
||||||
|
@ -206,6 +206,7 @@ def create_t_resources(context, objects, ext_net):
|
|||||||
# fix object before creation using the api replay code
|
# fix object before creation using the api replay code
|
||||||
orig_id = obj['id']
|
orig_id = obj['id']
|
||||||
prepare_object = getattr(prepare, "prepare_%s" % resource)
|
prepare_object = getattr(prepare, "prepare_%s" % resource)
|
||||||
|
# TODO(asarfaty): Add availability zones support too
|
||||||
obj_data = prepare_object(obj, direct_call=True)
|
obj_data = prepare_object(obj, direct_call=True)
|
||||||
enable_dhcp = False
|
enable_dhcp = False
|
||||||
# special cases for different objects before create:
|
# special cases for different objects before create:
|
||||||
|
94
vmware_nsx/tests/unit/nsx_p/test_api_replay.py
Normal file
94
vmware_nsx/tests/unit/nsx_p/test_api_replay.py
Normal file
@ -0,0 +1,94 @@
|
|||||||
|
# Copyright (c) 2019 OpenStack Foundation.
|
||||||
|
#
|
||||||
|
# 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 vmware_nsx.extensions import api_replay
|
||||||
|
from vmware_nsx.tests.unit.nsx_p import test_plugin
|
||||||
|
|
||||||
|
from neutron_lib.api import attributes
|
||||||
|
from neutron_lib.plugins import directory
|
||||||
|
from oslo_config import cfg
|
||||||
|
|
||||||
|
|
||||||
|
class TestApiReplay(test_plugin.NsxPTestL3NatTest):
|
||||||
|
|
||||||
|
def setUp(self, plugin=None, ext_mgr=None, service_plugins=None):
|
||||||
|
# enables api_replay_mode for these tests
|
||||||
|
cfg.CONF.set_override('api_replay_mode', True)
|
||||||
|
|
||||||
|
super(TestApiReplay, self).setUp()
|
||||||
|
|
||||||
|
def tearDown(self):
|
||||||
|
# disables api_replay_mode for these tests
|
||||||
|
cfg.CONF.set_override('api_replay_mode', False)
|
||||||
|
|
||||||
|
# remove the extension from the plugin
|
||||||
|
directory.get_plugin().supported_extension_aliases.remove(
|
||||||
|
api_replay.ALIAS)
|
||||||
|
|
||||||
|
# Revert the attributes map back to normal
|
||||||
|
for attr_name in ('ports', 'networks', 'security_groups',
|
||||||
|
'security_group_rules', 'routers', 'policies'):
|
||||||
|
attr_info = attributes.RESOURCES[attr_name]
|
||||||
|
attr_info['id']['allow_post'] = False
|
||||||
|
|
||||||
|
super(TestApiReplay, self).tearDown()
|
||||||
|
|
||||||
|
def test_create_port_specify_id(self):
|
||||||
|
specified_network_id = '555e762b-d7a1-4b44-b09b-2a34ada56c9f'
|
||||||
|
specified_port_id = 'e55e762b-d7a1-4b44-b09b-2a34ada56c9f'
|
||||||
|
network_res = self._create_network(self.fmt,
|
||||||
|
'test-network',
|
||||||
|
True,
|
||||||
|
arg_list=('id',),
|
||||||
|
id=specified_network_id)
|
||||||
|
network = self.deserialize(self.fmt, network_res)
|
||||||
|
self.assertEqual(specified_network_id, network['network']['id'])
|
||||||
|
port_res = self._create_port(self.fmt,
|
||||||
|
network['network']['id'],
|
||||||
|
arg_list=('id',),
|
||||||
|
id=specified_port_id)
|
||||||
|
port = self.deserialize(self.fmt, port_res)
|
||||||
|
self.assertEqual(specified_port_id, port['port']['id'])
|
||||||
|
|
||||||
|
def _create_router(self, fmt, tenant_id, name=None,
|
||||||
|
admin_state_up=None,
|
||||||
|
arg_list=None, **kwargs):
|
||||||
|
data = {'router': {'tenant_id': tenant_id}}
|
||||||
|
if name:
|
||||||
|
data['router']['name'] = name
|
||||||
|
if admin_state_up:
|
||||||
|
data['router']['admin_state_up'] = admin_state_up
|
||||||
|
for arg in (('admin_state_up', 'tenant_id') + (arg_list or ())):
|
||||||
|
# Arg must be present and not empty
|
||||||
|
if kwargs.get(arg):
|
||||||
|
data['router'][arg] = kwargs[arg]
|
||||||
|
router_req = self.new_create_request('routers', data, fmt)
|
||||||
|
return router_req.get_response(self.ext_api)
|
||||||
|
|
||||||
|
def test_create_update_router(self):
|
||||||
|
specified_router_id = '555e762b-d7a1-4b44-b09b-2a34ada56c9f'
|
||||||
|
router_res = self._create_router(self.fmt,
|
||||||
|
'test-tenant',
|
||||||
|
'test-rtr',
|
||||||
|
arg_list=('id',),
|
||||||
|
id=specified_router_id)
|
||||||
|
router = self.deserialize(self.fmt, router_res)
|
||||||
|
self.assertEqual(specified_router_id, router['router']['id'])
|
||||||
|
|
||||||
|
# This part tests _fixup_res_dict as well
|
||||||
|
body = self._update('routers', specified_router_id,
|
||||||
|
{'router': {'name': 'new_name'}})
|
||||||
|
body = self._show('routers', specified_router_id)
|
||||||
|
self.assertEqual(body['router']['name'], 'new_name')
|
Loading…
Reference in New Issue
Block a user