A set of Neutron drivers for the VMware NSX.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

1637 lines
65 KiB

  1. # Copyright 2020 VMware, Inc. All rights reserved.
  2. #
  3. # Licensed under the Apache License, Version 2.0 (the "License"); you may
  4. # not use this file except in compliance with the License. You may obtain
  5. # a copy of the License at
  6. #
  7. # http://www.apache.org/licenses/LICENSE-2.0
  8. #
  9. # Unless required by applicable law or agreed to in writing, software
  10. # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
  11. # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
  12. # License for the specific language governing permissions and limitations
  13. # under the License.
  14. import copy
  15. import time
  16. import logging
  17. import paramiko
  18. import tenacity
  19. from neutron_fwaas.db.firewall.v2 import firewall_db_v2
  20. from neutron_lib.callbacks import registry
  21. from neutron_lib import context
  22. from oslo_config import cfg
  23. from vmware_nsx.common import utils as nsx_utils
  24. from vmware_nsx.db import db
  25. from vmware_nsx.db import nsx_models
  26. from vmware_nsx.plugins.nsx_p import plugin as p_plugin
  27. from vmware_nsx.plugins.nsx_v3 import cert_utils
  28. from vmware_nsx.plugins.nsx_v3 import plugin as v3_plugin
  29. from vmware_nsx.plugins.nsx_v3 import utils as v3_plugin_utils
  30. from vmware_nsx.services.fwaas.nsx_p import fwaas_callbacks_v2
  31. from vmware_nsx.services.lbaas import lb_const
  32. from vmware_nsx.services.lbaas.nsx_p.implementation import lb_utils
  33. from vmware_nsx.shell.admin.plugins.common import constants
  34. from vmware_nsx.shell.admin.plugins.common import utils as admin_utils
  35. from vmware_nsx.shell.admin.plugins.nsxp.resources import utils as p_utils
  36. from vmware_nsx.shell.admin.plugins.nsxv3.resources import utils
  37. from vmware_nsx.shell import resources as shell
  38. from vmware_nsxlib.v3 import core_resources as nsx_resources
  39. from vmware_nsxlib.v3 import exceptions as nsxlib_exc
  40. from vmware_nsxlib.v3 import load_balancer as nsxlib_lb
  41. from vmware_nsxlib.v3 import nsx_constants
  42. from vmware_nsxlib.v3.policy import constants as policy_constants
  43. from vmware_nsxlib.v3.policy import core_resources as policy_resources
  44. from vmware_nsxlib.v3.policy import utils as policy_utils
  45. LOG = logging.getLogger(__name__)
  46. POLICY_API_STATUS_FAILED = 'FAILED'
  47. POLICY_API_STATUS_SUCCESS = 'SUCCESS'
  48. POLICY_API_STATUS_IN_PROGRESS = 'PAUSING'
  49. POLICY_API_STATUS_PAUSED = 'PAUSED'
  50. POLICY_API_STATUS_READY = 'NOT_STARTED'
  51. STATUS_ALLOW_MIGRATION_REQ = set([
  52. POLICY_API_STATUS_SUCCESS,
  53. POLICY_API_STATUS_READY
  54. ])
  55. MIGRATE_LIMIT_NO_LIMIT = 0
  56. MIGRATE_LIMIT_TIER0 = 1
  57. MIGRATE_LIMIT_TIER0_PORTS = 1000
  58. MIGRATE_LIMIT_TIER1 = 1000
  59. MIGRATE_LIMIT_TIER1_PORTS = 1000
  60. MIGRATE_LIMIT_NAT = 1500
  61. MIGRATE_LIMIT_DHCP_SERVER = 1500
  62. MIGRATE_LIMIT_MD_PROXY = 1500
  63. MIGRATE_LIMIT_SWITCH_PROFILE = 1500
  64. MIGRATE_LIMIT_LOGICAL_SWITCH = 500
  65. MIGRATE_LIMIT_LOGICAL_PORT = 1500
  66. MIGRATE_LIMIT_NS_GROUP = 2000
  67. MIGRATE_LIMIT_SECTION_AND_RULES = 1500
  68. MIGRATE_LIMIT_LB_SERVICE = 2000
  69. MIGRATE_LIMIT_LB_VIRTUAL_SERVER = 2000
  70. MIGRATE_LIMIT_LB_MONITOR = 1500
  71. MIGRATE_LIMIT_LB_POOL = 1500
  72. MIGRATE_LIMIT_LB_APP_PROFILE = 2000
  73. MIGRATE_LIMIT_LB_PER_PROFILE = 2000
  74. MIGRATE_LIMIT_CERT = 1500
  75. COMPONENT_STATUS_ALREADY_MIGRATED = 1
  76. COMPONENT_STATUS_OK = 2
  77. ROLLBACK_DATA = []
  78. EDGE_FW_SEQ = 1
  79. DFW_SEQ = 1
  80. SERVICE_UP_RETRIES = 30
  81. def start_migration_process(nsxlib):
  82. """Notify the manager that the migration process is starting"""
  83. return nsxlib.client.url_post(
  84. "migration/mp-to-policy/workflow?action=INITIATE", None)
  85. def end_migration_process(nsxlib):
  86. """Notify the manager that the migration process has ended"""
  87. return nsxlib.client.url_post(
  88. "migration/mp-to-policy/workflow?action=DONE", None)
  89. def send_migration_request(nsxlib, body):
  90. return nsxlib.client.url_post("migration/mp-to-policy", body)
  91. def send_rollback_request(nsxlib, body):
  92. #TODO(asarfaty): Rollback can take very long, especially for firewall
  93. # sections. In this case backup-restore might be better
  94. return nsxlib.client.url_post("migration/mp-to-policy/rollback", body)
  95. def send_migration_plan_action(nsxlib, action):
  96. return nsxlib.client.url_post("migration/plan?action=%s" % action, None)
  97. def get_migration_status(nsxlib, silent=False):
  98. return nsxlib.client.get("migration/status-summary",
  99. silent=silent)
  100. def change_migration_service_status(start=True, nsxlib=None):
  101. """Enable/Disable the migration service on the NSX manager
  102. using SSH command
  103. """
  104. # TODO(asarfaty): Is there an api for that? or use sshpass
  105. action = 'start' if start else 'stop'
  106. command = "%s service migration-coordinator" % action
  107. LOG.info("Going to %s the migration service on the NSX manager by "
  108. "SSHing the manager and running '%s'", action, command)
  109. host = cfg.CONF.nsx_v3.nsx_api_managers[0]
  110. user = cfg.CONF.nsx_v3.nsx_api_user[0]
  111. passwd = cfg.CONF.nsx_v3.nsx_api_password[0]
  112. ssh = paramiko.SSHClient()
  113. ssh.load_system_host_keys()
  114. ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
  115. ssh.connect(host, username=user, password=passwd)
  116. ssh.exec_command(command)
  117. if start and nsxlib:
  118. LOG.info("Waiting for the service to be up...")
  119. start_time = time.time()
  120. @tenacity.retry(reraise=True,
  121. retry=tenacity.retry_if_exception_type(Exception),
  122. wait=tenacity.wait_exponential(multiplier=0.5, max=2),
  123. stop=tenacity.stop_after_attempt(SERVICE_UP_RETRIES))
  124. def get_migration_status_with_retry(nsxlib):
  125. get_migration_status(nsxlib, silent=True)
  126. try:
  127. get_migration_status_with_retry(nsxlib)
  128. except Exception:
  129. raise Exception("The migration service did not get up after %s "
  130. "retries" % SERVICE_UP_RETRIES)
  131. elapsed_time = time.time() - start_time
  132. LOG.info("The service is up (waited %s seconds)", elapsed_time)
  133. def ensure_migration_state_ready(nsxlib, with_abort=False):
  134. try:
  135. status = get_migration_status(nsxlib, silent=True)
  136. except Exception as e:
  137. if with_abort:
  138. change_migration_service_status(start=True, nsxlib=nsxlib)
  139. return ensure_migration_state_ready(nsxlib)
  140. LOG.debug("Failed to get migration status: %s", e)
  141. return False
  142. if status["overall_migration_status"] not in STATUS_ALLOW_MIGRATION_REQ:
  143. LOG.error("Migration status not ready: %s", status)
  144. if with_abort:
  145. send_migration_plan_action(nsxlib, 'abort')
  146. return ensure_migration_state_ready(
  147. nsxlib, with_abort=with_abort)
  148. else:
  149. return False
  150. return True
  151. def verify_component_status(nsxlib, component_number):
  152. status = get_migration_status(nsxlib)
  153. if (status['component_status'][component_number]['status'] ==
  154. POLICY_API_STATUS_FAILED):
  155. # If it's a duplicate migration request, pass the verification
  156. if ('is duplicate or already migrated' in
  157. status['component_status'][component_number]['details'] and
  158. component_number == 0):
  159. # Success that indicates resource migration is already done
  160. return COMPONENT_STATUS_ALREADY_MIGRATED
  161. # bad state. abort, mark as fail, and go to next request
  162. raise Exception("The migration server returned with FAILURE status. "
  163. "Details: %s", status)
  164. # Success
  165. return COMPONENT_STATUS_OK
  166. def wait_on_overall_migration_status_to_pause(nsxlib):
  167. while True:
  168. status = get_migration_status(nsxlib)
  169. migration_status = status.get('overall_migration_status')
  170. if (migration_status == POLICY_API_STATUS_PAUSED or
  171. migration_status == POLICY_API_STATUS_SUCCESS):
  172. break
  173. time.sleep(1)
  174. def printable_resource_name(resource):
  175. name = resource.get('display_name')
  176. if name:
  177. try:
  178. name = str(name)
  179. except UnicodeEncodeError:
  180. name = name.encode('ascii', 'ignore')
  181. res_id = resource.get('id')
  182. if name == res_id:
  183. return name
  184. return "%s (%s)" % (name, resource.get('id'))
  185. def get_resource_migration_data(nsxlib_resource, neutron_id_tags,
  186. resource_type, resource_condition=None,
  187. printable_name=None, policy_resource_get=None,
  188. policy_id_callback=None,
  189. metadata_callback=None,
  190. skip_policy_path_check=False,
  191. nsxlib_list_args=None):
  192. if not printable_name:
  193. printable_name = resource_type
  194. LOG.debug("Getting data for MP %s", printable_name)
  195. if nsxlib_list_args:
  196. resources = nsxlib_resource.list(**nsxlib_list_args)
  197. else:
  198. resources = nsxlib_resource.list()
  199. if not isinstance(resources, list):
  200. # the nsxlib resources list return inconsistent type of result
  201. resources = resources.get('results', [])
  202. entries = []
  203. for resource in resources:
  204. name_and_id = printable_resource_name(resource)
  205. policy_id = resource['id']
  206. # Go over tags and find the neutron id
  207. neutron_id = None
  208. found_policy_path = False
  209. for tag in resource.get('tags', []):
  210. if tag['scope'] == 'policyPath':
  211. # This is already a policy resource
  212. found_policy_path = True
  213. if neutron_id_tags and tag['scope'] in neutron_id_tags:
  214. neutron_id = tag['tag']
  215. if not skip_policy_path_check and found_policy_path:
  216. LOG.debug("Skipping %s %s as it is already a policy "
  217. "resource", printable_name, name_and_id)
  218. continue
  219. if neutron_id_tags:
  220. if not neutron_id:
  221. # Not a neutron resource
  222. LOG.debug("Skipping %s %s as it is not a neutron resource",
  223. printable_name, name_and_id)
  224. continue
  225. policy_id = neutron_id
  226. if resource_condition:
  227. if not resource_condition(resource):
  228. LOG.debug("Skipping %s %s as it does not match the neutron "
  229. "condition", printable_name, name_and_id)
  230. continue
  231. if policy_id_callback:
  232. # Callback to change the policy id
  233. policy_id = policy_id_callback(resource, policy_id)
  234. if policy_id and policy_resource_get:
  235. # filter out resources that already exit on policy!
  236. try:
  237. policy_resource_get(policy_id, silent=True)
  238. except nsxlib_exc.ResourceNotFound:
  239. pass
  240. else:
  241. LOG.debug("Skipping %s %s as it already exists on the "
  242. "policy backend", printable_name, name_and_id)
  243. continue
  244. LOG.debug("Adding data for %s %s, policy-id %s",
  245. printable_name, name_and_id, policy_id)
  246. entry = {'manager_id': resource['id']}
  247. if policy_id:
  248. entry['policy_id'] = policy_id
  249. if metadata_callback:
  250. metadata_callback(entry, policy_id, resource)
  251. entries.append(entry)
  252. return entries
  253. def migrate_objects(nsxlib, data, use_admin=False):
  254. if not ensure_migration_state_ready(nsxlib):
  255. raise Exception("The migration server is not ready")
  256. migration_body = {"migration_data": [data]}
  257. # Update the principal identity for the policy resources
  258. # use 'admin' for predefined objects, and the opestack configured
  259. # user/identity for openstack resources
  260. if use_admin:
  261. user = 'admin'
  262. elif cfg.CONF.nsx_v3.nsx_use_client_auth:
  263. user = cert_utils.NSX_OPENSTACK_IDENTITY
  264. else:
  265. user = cfg.CONF.nsx_v3.nsx_api_user[0]
  266. migration_body['setup_details'] = {
  267. 'principal_identity': user}
  268. LOG.info("Migrating %d %s objects with principal_identity %s",
  269. len(data['resource_ids']), data['type'], user)
  270. LOG.debug("Migration body : %s", migration_body)
  271. send_migration_request(nsxlib, migration_body)
  272. # send the start action
  273. send_migration_plan_action(nsxlib, 'start')
  274. # wait until the overall_migration_status is SUCCESS
  275. wait_on_overall_migration_status_to_pause(nsxlib)
  276. # verify first component status
  277. success_code = verify_component_status(nsxlib, 0)
  278. if success_code == COMPONENT_STATUS_ALREADY_MIGRATED:
  279. return True
  280. # send the continue action
  281. send_migration_plan_action(nsxlib, 'continue')
  282. # wait until the overall_migration_status is SUCCESS
  283. wait_on_overall_migration_status_to_pause(nsxlib)
  284. # verify second component status (Will raise in case of error)
  285. try:
  286. verify_component_status(nsxlib, 1)
  287. except Exception as e:
  288. raise e
  289. else:
  290. global ROLLBACK_DATA
  291. ROLLBACK_DATA.append(data)
  292. return True
  293. def migrate_resource(nsxlib, resource_type, entries,
  294. limit=MIGRATE_LIMIT_NO_LIMIT,
  295. count_internals=False, use_admin=False):
  296. # Call migrate_resource with the part of resources we need by the limit
  297. if not entries:
  298. LOG.info("No %s to migrate", resource_type)
  299. return
  300. LOG.info("Going to migrate %d %s objects in groups of max %s",
  301. len(entries), resource_type, limit)
  302. if limit == MIGRATE_LIMIT_NO_LIMIT:
  303. migrate_objects(nsxlib, {'type': resource_type,
  304. 'resource_ids': entries},
  305. use_admin=use_admin)
  306. else:
  307. if count_internals:
  308. # Limit the total number of resources, including internal ones
  309. counter = 0
  310. entries_to_migrate = []
  311. for index in range(0, len(entries)):
  312. addition_size = 1 + len(entries[index].get('linked_ids', []))
  313. if addition_size > limit:
  314. # Unsupported size of resource
  315. raise Exception("%s size is over the allowed limit of "
  316. "%s" % (resource_type, limit))
  317. if counter + addition_size > limit:
  318. # Migrate what was accumulated so far
  319. migrate_objects(nsxlib,
  320. {'type': resource_type,
  321. 'resource_ids': entries_to_migrate},
  322. use_admin=use_admin)
  323. # Start a new accumulation
  324. counter = addition_size
  325. entries_to_migrate = [entries[index]]
  326. else:
  327. # Keep accumulating
  328. counter = counter + addition_size
  329. entries_to_migrate.append(entries[index])
  330. if entries_to_migrate:
  331. # Migrate the left overs
  332. migrate_objects(nsxlib,
  333. {'type': resource_type,
  334. 'resource_ids': entries_to_migrate},
  335. use_admin=use_admin)
  336. else:
  337. for index in range(0, len(entries), limit):
  338. migrate_objects(nsxlib,
  339. {'type': resource_type,
  340. 'resource_ids': entries[index:index + limit]},
  341. use_admin=use_admin)
  342. def get_configured_values(plugin, az_attribute):
  343. values = []
  344. for az in plugin.get_azs_list():
  345. az_values = getattr(az, az_attribute)
  346. if isinstance(az_values, list):
  347. values.extend(az_values)
  348. else:
  349. values.append(az_values)
  350. return values
  351. def get_neurton_tier0s(plugin):
  352. return get_configured_values(plugin, '_default_tier0_router')
  353. def migrate_tier0s(nsxlib, nsxpolicy, plugin):
  354. # First prepare a list of neutron related tier0s from the config
  355. neutron_t0s = get_neurton_tier0s(plugin)
  356. # Add tier0s used specifically in external networks
  357. ctx = context.get_admin_context()
  358. with ctx.session.begin(subtransactions=True):
  359. bindings = ctx.session.query(
  360. nsx_models.TzNetworkBinding).filter_by(
  361. binding_type='l3_ext').all()
  362. for bind in bindings:
  363. if bind.phy_uuid not in neutron_t0s:
  364. neutron_t0s.append(bind.phy_uuid)
  365. def cond(resource):
  366. return (resource.get('router_type', '') == 'TIER0' and
  367. resource.get('id') in neutron_t0s)
  368. entries = get_resource_migration_data(
  369. nsxlib.logical_router, None,
  370. 'TIER0', resource_condition=cond,
  371. policy_resource_get=nsxpolicy.tier0.get,
  372. nsxlib_list_args={'router_type': nsx_constants.ROUTER_TYPE_TIER0})
  373. migrate_resource(nsxlib, 'TIER0', entries, MIGRATE_LIMIT_TIER0,
  374. use_admin=True)
  375. migrated_tier0s = [entry['manager_id'] for entry in entries]
  376. # Create a list of public switches connected to the tier0s to migrate later
  377. public_switches = []
  378. for tier0 in neutron_t0s:
  379. uplink_port = nsxlib.logical_router_port.get_tier0_uplink_port(tier0)
  380. if uplink_port:
  381. # Get the external LS id from the uplink port
  382. port_id = uplink_port['linked_logical_switch_port_id']['target_id']
  383. port = nsxlib.logical_port.get(port_id)
  384. public_switches.append(port['logical_switch_id'])
  385. return public_switches, migrated_tier0s
  386. def is_neutron_resource(resource):
  387. # Return True if the resource has the neutron marking tag
  388. for tag in resource.get('tags', []):
  389. if tag.get('scope') == 'os-api-version':
  390. return True
  391. return False
  392. def migrate_switch_profiles(nsxlib, nsxpolicy, plugin):
  393. """Return all types of neutron switching profiles"""
  394. # Build a condition for each type of switching profiles.
  395. # Note(asarfaty): system owned profiles should also be migrated as they are
  396. # missing from policy
  397. # Include switch profiles that are in the nsx.ini
  398. conf_profiles = get_configured_values(plugin, 'switching_profiles')
  399. # Add other switch profiles that might be used by neutron ports in the past
  400. port_profiles = set()
  401. ports = nsxlib.logical_port.list()['results']
  402. for port in ports:
  403. # Check that it is a neutron port
  404. if not is_neutron_resource(port):
  405. continue
  406. for prof in port.get('switching_profile_ids', []):
  407. port_profiles.add(prof['value'])
  408. # Black list neuron & system profiles that should not be migrated
  409. names_black_list = [v3_plugin_utils.NSX_V3_DHCP_PROFILE_NAME,
  410. 'ServiceInsertion_MacManagement_Profile']
  411. def get_cond(resource_type):
  412. def cond(resource):
  413. return (resource.get('resource_type') == resource_type and
  414. resource.get('display_name') not in names_black_list and
  415. (resource.get('id') in conf_profiles or
  416. resource.get('id') in port_profiles or
  417. resource.get('_system_owned', True) or
  418. is_neutron_resource(resource)))
  419. return cond
  420. def get_policy_id_callback(res, policy_id):
  421. # In case of plugin init profiles: give it the id the policy plugin
  422. # will use
  423. mapping = {v3_plugin.NSX_V3_MAC_LEARNING_PROFILE_NAME:
  424. p_plugin.MAC_DISCOVERY_PROFILE_ID,
  425. v3_plugin_utils.NSX_V3_PSEC_PROFILE_NAME:
  426. p_plugin.SPOOFGUARD_PROFILE_ID}
  427. if mapping.get(res.get('display_name')):
  428. return mapping[res['display_name']]
  429. # QoS profiles should get the neutron policy id
  430. for tag in res.get('tags', []):
  431. if tag['scope'] == 'os-neutron-qos-id':
  432. policy_id = tag['tag']
  433. return policy_id
  434. entries = get_resource_migration_data(
  435. nsxlib.switching_profile, None,
  436. 'SPOOFGUARD_PROFILES',
  437. resource_condition=get_cond(
  438. nsx_resources.SwitchingProfileTypes.SPOOF_GUARD),
  439. policy_resource_get=nsxpolicy.spoofguard_profile.get,
  440. policy_id_callback=get_policy_id_callback)
  441. migrate_resource(nsxlib, 'SPOOFGUARD_PROFILES', entries,
  442. MIGRATE_LIMIT_SWITCH_PROFILE)
  443. entries = get_resource_migration_data(
  444. nsxlib.switching_profile, None,
  445. 'MACDISCOVERY_PROFILES',
  446. resource_condition=get_cond(
  447. nsx_resources.SwitchingProfileTypes.MAC_LEARNING),
  448. policy_resource_get=nsxpolicy.mac_discovery_profile.get,
  449. policy_id_callback=get_policy_id_callback)
  450. migrate_resource(nsxlib, 'MACDISCOVERY_PROFILES', entries,
  451. MIGRATE_LIMIT_SWITCH_PROFILE)
  452. entries = get_resource_migration_data(
  453. nsxlib.switching_profile, None,
  454. 'SEGMENT_SECURITY_PROFILES',
  455. resource_condition=get_cond(
  456. nsx_resources.SwitchingProfileTypes.SWITCH_SECURITY),
  457. policy_resource_get=nsxpolicy.segment_security_profile.get,
  458. policy_id_callback=get_policy_id_callback)
  459. migrate_resource(nsxlib, 'SEGMENT_SECURITY_PROFILES', entries,
  460. MIGRATE_LIMIT_SWITCH_PROFILE)
  461. entries = get_resource_migration_data(
  462. nsxlib.switching_profile, None,
  463. 'QOS_PROFILES',
  464. resource_condition=get_cond(
  465. nsx_resources.SwitchingProfileTypes.QOS),
  466. policy_resource_get=nsxpolicy.qos_profile.get,
  467. policy_id_callback=get_policy_id_callback)
  468. migrate_resource(nsxlib, 'QOS_PROFILES', entries,
  469. MIGRATE_LIMIT_SWITCH_PROFILE)
  470. entries = get_resource_migration_data(
  471. nsxlib.switching_profile, None,
  472. 'IPDISCOVERY_PROFILES',
  473. resource_condition=get_cond(
  474. nsx_resources.SwitchingProfileTypes.IP_DISCOVERY),
  475. policy_resource_get=nsxpolicy.ip_discovery_profile.get,
  476. policy_id_callback=get_policy_id_callback)
  477. migrate_resource(nsxlib, 'IPDISCOVERY_PROFILES', entries,
  478. MIGRATE_LIMIT_SWITCH_PROFILE)
  479. def migrate_md_proxies(nsxlib, nsxpolicy, plugin):
  480. neutron_md = get_configured_values(plugin, '_native_md_proxy_uuid')
  481. # Add other mdproxies that might be used by neutron networks in the past
  482. ports = nsxlib.logical_port.list()['results']
  483. for port in ports:
  484. # Check that it is a neutron port
  485. if not is_neutron_resource(port):
  486. continue
  487. if (port.get('attachment') and
  488. port['attachment'].get('attachment_type') == 'METADATA_PROXY'):
  489. mdproxy_id = port['attachment'].get('id')
  490. if mdproxy_id not in neutron_md:
  491. neutron_md.append(port['attachment'].get('id'))
  492. def cond(resource):
  493. return resource.get('id') in neutron_md
  494. entries = get_resource_migration_data(
  495. nsxlib.native_md_proxy, None,
  496. 'METADATA_PROXY',
  497. resource_condition=cond,
  498. policy_resource_get=nsxpolicy.md_proxy.get)
  499. migrate_resource(nsxlib, 'METADATA_PROXY', entries,
  500. MIGRATE_LIMIT_MD_PROXY, use_admin=True)
  501. def migrate_networks(nsxlib, nsxpolicy, plugin, public_switches):
  502. # Get a list of nsx-net provider networks to migrate
  503. # Those networks have no tags, and should keep the same id in policy
  504. nsx_networks = []
  505. ctx = context.get_admin_context()
  506. with ctx.session.begin(subtransactions=True):
  507. bindings = ctx.session.query(
  508. nsx_models.TzNetworkBinding).filter_by(
  509. binding_type=nsx_utils.NsxV3NetworkTypes.NSX_NETWORK).all()
  510. for bind in bindings:
  511. nsx_networks.append(bind.phy_uuid)
  512. def cond(resource):
  513. return (resource.get('id', '') in nsx_networks or
  514. resource.get('id', '') in public_switches or
  515. is_neutron_resource(resource))
  516. def get_policy_id(resource, policy_id):
  517. if resource['id'] in nsx_networks:
  518. # Keep original ID
  519. return resource['id']
  520. if resource['id'] in public_switches:
  521. # Keep original ID
  522. return resource['id']
  523. for tag in resource.get('tags', []):
  524. # Use the neutron ID
  525. if tag['scope'] == 'os-neutron-net-id':
  526. return tag['tag']
  527. def add_metadata(entry, policy_id, resource):
  528. # Add dhcp-v4 static bindings
  529. network_id = None
  530. for tag in resource.get('tags', []):
  531. # Use the neutron ID
  532. if tag['scope'] == 'os-neutron-net-id':
  533. network_id = tag['tag']
  534. break
  535. if not network_id:
  536. return
  537. metadata = []
  538. ctx = context.get_admin_context()
  539. port_filters = {'network_id': [network_id]}
  540. network_ports = plugin.get_ports(ctx, filters=port_filters)
  541. for port in network_ports:
  542. bindings = db.get_nsx_dhcp_bindings(ctx.session, port['id'])
  543. if bindings:
  544. # Should be only 1
  545. metadata.append({
  546. 'key': 'v4-static-binding%s' % bindings[0].nsx_binding_id,
  547. 'value': port['id'] + '-ipv4'})
  548. entry['metadata'] = metadata
  549. entries = get_resource_migration_data(
  550. nsxlib.logical_switch, [],
  551. 'LOGICAL_SWITCH',
  552. resource_condition=cond,
  553. policy_resource_get=nsxpolicy.segment.get,
  554. policy_id_callback=get_policy_id,
  555. metadata_callback=add_metadata)
  556. migrate_resource(nsxlib, 'LOGICAL_SWITCH', entries,
  557. MIGRATE_LIMIT_LOGICAL_SWITCH)
  558. def migrate_ports(nsxlib, nsxpolicy, plugin):
  559. # For nsx networks support, keep a mapping of neutron id and MP id
  560. nsx_networks = {}
  561. ctx = context.get_admin_context()
  562. with ctx.session.begin(subtransactions=True):
  563. bindings = ctx.session.query(
  564. nsx_models.TzNetworkBinding).filter_by(
  565. binding_type='nsx-net').all()
  566. for bind in bindings:
  567. nsx_networks[bind.network_id] = bind.phy_uuid
  568. def get_policy_port(port_id, silent=False):
  569. # Get the segment id from neutron
  570. ctx = context.get_admin_context()
  571. neutron_port = plugin.get_port(ctx, port_id)
  572. net_id = neutron_port['network_id']
  573. if net_id in nsx_networks:
  574. segment_id = nsx_networks[net_id]
  575. else:
  576. segment_id = net_id
  577. return nsxpolicy.segment_port.get(segment_id, port_id, silent=silent)
  578. def add_metadata(entry, policy_id, resource):
  579. # Add binding maps with 'DEFAULT' key
  580. entry['metadata'] = [{'key': 'security-profile-binding-maps-id',
  581. 'value': policy_resources.DEFAULT_MAP_ID},
  582. {'key': 'discovery-profile-binding-maps-id',
  583. 'value': policy_resources.DEFAULT_MAP_ID},
  584. {'key': 'qos-profile-binding-maps-id',
  585. 'value': policy_resources.DEFAULT_MAP_ID}]
  586. entries = get_resource_migration_data(
  587. nsxlib.logical_port, ['os-neutron-port-id'],
  588. 'LOGICAL_PORT',
  589. policy_resource_get=get_policy_port,
  590. metadata_callback=add_metadata)
  591. migrate_resource(nsxlib, 'LOGICAL_PORT', entries,
  592. MIGRATE_LIMIT_LOGICAL_PORT)
  593. def migrate_routers(nsxlib, nsxpolicy):
  594. entries = get_resource_migration_data(
  595. nsxlib.logical_router,
  596. ['os-neutron-router-id'],
  597. 'TIER1',
  598. policy_resource_get=nsxpolicy.tier1.get,
  599. nsxlib_list_args={'router_type': nsx_constants.ROUTER_TYPE_TIER1})
  600. migrate_resource(nsxlib, 'TIER1', entries, MIGRATE_LIMIT_TIER1)
  601. migrated_routers = [entry['manager_id'] for entry in entries]
  602. return migrated_routers
  603. def _get_subnet_by_cidr(subnets, cidr):
  604. for subnet in subnets:
  605. if subnet['cidr'] == cidr:
  606. return subnet['id']
  607. def migrate_routers_config(nsxlib, nsxpolicy, plugin, migrated_routers):
  608. """Migrate advanced configuration of neutron Tier-1s
  609. This will use the list of Tier-1s migrated earlier
  610. """
  611. # Migrate all the centralized router ports and static routes for tier1
  612. # routers without specifying ids
  613. def get_policy_id(resource, policy_id):
  614. # No policy id needed here
  615. return
  616. def cond(resource):
  617. # Import ports only for the routers that were currently migrated
  618. # because there is no easy way to verify what was already migrated
  619. return resource['id'] in migrated_routers
  620. def add_metadata(entry, policy_id, resource):
  621. # Add router interfaces Ids
  622. ctx = context.get_admin_context()
  623. metadata = []
  624. mp_rtr_id = resource['id']
  625. router_ports = nsxlib.logical_router_port.get_by_router_id(mp_rtr_id)
  626. for port in router_ports:
  627. if 'linked_logical_switch_port_id' in port:
  628. lsp_id = port['linked_logical_switch_port_id']['target_id']
  629. lsp = nsxlib.logical_port.get(lsp_id)
  630. ls_id = lsp['logical_switch_id']
  631. if ls_id:
  632. neutron_net_ids = plugin._get_neutron_net_ids_by_nsx_id(
  633. ctx, ls_id)
  634. if neutron_net_ids:
  635. # Should be only 1
  636. metadata.append({'key': port['id'],
  637. 'value': neutron_net_ids[0]})
  638. # Add static routes ids
  639. static_routes = nsxlib.logical_router.list_static_routes(
  640. mp_rtr_id)['results']
  641. for route in static_routes:
  642. policy_id = "%s-%s" % (route['network'].replace('/', '_'),
  643. route['next_hops'][0]['ip_address'])
  644. metadata.append({'key': route['id'],
  645. 'value': policy_id})
  646. # Add locale-service id as <routerid>-0
  647. policy_id = None
  648. for tag in resource.get('tags', []):
  649. if tag['scope'] == 'os-neutron-router-id':
  650. policy_id = tag['tag']
  651. if policy_id:
  652. metadata.append({'key': 'localeServiceId',
  653. 'value': "%s-0" % policy_id})
  654. entry['metadata'] = metadata
  655. entries = get_resource_migration_data(
  656. nsxlib.logical_router,
  657. ['os-neutron-router-id'],
  658. 'TIER1_LOGICAL_ROUTER_PORT',
  659. policy_id_callback=get_policy_id,
  660. resource_condition=cond,
  661. metadata_callback=add_metadata,
  662. skip_policy_path_check=True,
  663. nsxlib_list_args={'router_type': nsx_constants.ROUTER_TYPE_TIER1})
  664. migrate_resource(nsxlib, 'TIER1_LOGICAL_ROUTER_PORT', entries,
  665. MIGRATE_LIMIT_TIER1_PORTS)
  666. # Migrate NAT rules per neutron tier1
  667. entries = []
  668. tier1s = nsxlib.logical_router.list(
  669. router_type=nsx_constants.ROUTER_TYPE_TIER1)['results']
  670. ctx = context.get_admin_context()
  671. for tier1 in tier1s:
  672. # skip routers that were not migrated in this script call
  673. tier1_mp_id = tier1['id']
  674. if tier1_mp_id not in migrated_routers:
  675. continue
  676. # skip non-neutron routers
  677. tier1_neutron_id = None
  678. for tag in tier1.get('tags', []):
  679. if tag['scope'] == 'os-neutron-router-id':
  680. tier1_neutron_id = tag['tag']
  681. break
  682. if not tier1_neutron_id:
  683. continue
  684. # Migrate each existing NAT rule, with the parameters the policy
  685. # plugin would have set
  686. router_subnets = plugin._load_router_subnet_cidrs_from_db(
  687. ctx, tier1_neutron_id)
  688. nat_rules = nsxlib.logical_router.list_nat_rules(
  689. tier1_mp_id)['results']
  690. for rule in nat_rules:
  691. # NO_DNAT rules for subnets
  692. if rule['action'] == 'NO_DNAT':
  693. seq_num = p_plugin.NAT_RULE_PRIORITY_GW
  694. cidr = rule['match_destination_network']
  695. subnet_id = _get_subnet_by_cidr(router_subnets, cidr)
  696. if not subnet_id:
  697. LOG.error("Could not find subnet with cidr %s matching "
  698. "NO_DNAT rule %s tier1 %s",
  699. cidr, rule['id'], tier1_neutron_id)
  700. continue
  701. policy_id = 'ND-' + subnet_id
  702. # SNAT rules for subnet or fip
  703. elif rule['action'] == 'SNAT':
  704. cidr = rule['match_source_network']
  705. if '/' in cidr:
  706. seq_num = p_plugin.NAT_RULE_PRIORITY_GW
  707. subnet_id = _get_subnet_by_cidr(router_subnets, cidr)
  708. if not subnet_id:
  709. LOG.error("Could not find subnet with cidr %s "
  710. "matching SNAT rule %s tier1 %s",
  711. cidr, rule['id'], tier1_neutron_id)
  712. continue
  713. policy_id = 'S-' + subnet_id
  714. else:
  715. # FIP rule
  716. seq_num = p_plugin.NAT_RULE_PRIORITY_FIP
  717. fip_ip = rule['translated_network']
  718. filters = {'floating_ip_address': [fip_ip]}
  719. fips = plugin.get_floatingips(ctx, filters)
  720. if not fips:
  721. LOG.error("Could not find FIP with ip %s matching "
  722. "SNAT rule %s tier1 %s",
  723. fip_ip, rule['id'], tier1_neutron_id)
  724. continue
  725. policy_id = 'S-' + fips[0]['id']
  726. # DNAT rules for fip
  727. elif rule['action'] == 'DNAT':
  728. # FIP rule
  729. seq_num = p_plugin.NAT_RULE_PRIORITY_FIP
  730. fip_ip = rule['match_destination_network']
  731. filters = {'floating_ip_address': [fip_ip]}
  732. fips = plugin.get_floatingips(ctx, filters)
  733. if not fips:
  734. LOG.error("Could not find FIP with ip %s matching DNAT "
  735. "rule %s tier1 %s",
  736. fip_ip, rule['id'], tier1_neutron_id)
  737. continue
  738. policy_id = 'D-' + fips[0]['id']
  739. else:
  740. LOG.error("Unknown NAT action %s for rule %s tier1 %s",
  741. rule['action'], rule['id'], tier1_neutron_id)
  742. continue
  743. entry = {'manager_id': rule['id'],
  744. 'policy_id': policy_id,
  745. 'metadata': [{'key': 'SEQUENCE_NUMBER',
  746. 'value': seq_num}],
  747. 'linked_ids': [{'key': 'TIER1',
  748. 'value': tier1_mp_id}]}
  749. entries.append(entry)
  750. migrate_resource(nsxlib, 'NAT', entries,
  751. MIGRATE_LIMIT_NAT)
  752. def migrate_tier0_config(nsxlib, nsxpolicy, tier0s):
  753. """Migrate ports and config for the already migrated Tier0s"""
  754. entries = []
  755. for tier0 in tier0s:
  756. uplink_port = nsxlib.logical_router_port.get_tier0_uplink_port(tier0)
  757. if uplink_port:
  758. entries.append({'manager_id': uplink_port['id']})
  759. migrate_resource(nsxlib, 'TIER0_LOGICAL_ROUTER_PORT', entries,
  760. MIGRATE_LIMIT_TIER0_PORTS, use_admin=True)
  761. def get_policy_id(resource, policy_id):
  762. # No policy id needed here
  763. return
  764. def cond(resource):
  765. # Import config only for the routers that were currently migrated
  766. # because there is no easy way to verify what was already migrated
  767. return resource['id'] in tier0s
  768. entries = get_resource_migration_data(
  769. nsxlib.logical_router, [],
  770. 'TIER0_LOGICAL_ROUTER_CONFIG',
  771. policy_id_callback=get_policy_id,
  772. resource_condition=cond,
  773. skip_policy_path_check=True,
  774. nsxlib_list_args={'router_type': nsx_constants.ROUTER_TYPE_TIER0})
  775. migrate_resource(nsxlib, 'TIER0_LOGICAL_ROUTER_CONFIG', entries,
  776. MIGRATE_LIMIT_TIER0, use_admin=True)
  777. def migrate_groups(nsxlib, nsxpolicy):
  778. """Migrate NS groups of neutron defined security groups and predefined at
  779. plugin init
  780. """
  781. def get_policy_id_callback(res, policy_id):
  782. # In case of plugin init groups: give it the id the policy plugin
  783. # will use
  784. if res.get('display_name') == \
  785. v3_plugin.NSX_V3_FW_DEFAULT_NS_GROUP:
  786. return p_plugin.NSX_P_DEFAULT_GROUP
  787. if res.get('display_name') == \
  788. v3_plugin.NSX_V3_EXCLUDED_PORT_NSGROUP_NAME:
  789. return p_plugin.NSX_P_EXCLUDE_LIST_GROUP
  790. return policy_id
  791. def get_policy_group(group_id, silent=False):
  792. return nsxpolicy.group.get(policy_constants.DEFAULT_DOMAIN, group_id,
  793. silent=silent)
  794. entries = get_resource_migration_data(
  795. nsxlib.ns_group,
  796. ['os-neutron-secgr-id', 'os-neutron-id'],
  797. 'NS_GROUP',
  798. policy_resource_get=get_policy_group,
  799. policy_id_callback=get_policy_id_callback)
  800. migrate_resource(nsxlib, 'NS_GROUP', entries, MIGRATE_LIMIT_NS_GROUP)
  801. def dfw_migration_cond(resource):
  802. return (resource.get('enforced_on') == 'VIF' and
  803. resource.get('category') == 'Default' and
  804. resource.get('section_type') == 'LAYER3' and
  805. not resource.get('is_default') and
  806. # Migrate only DFW sections only and no edge FW sections
  807. 'applied_tos' in resource and
  808. resource['applied_tos'][0].get('target_type', '') == 'NSGroup')
  809. def migrate_dfw_sections(nsxlib, nsxpolicy, plugin):
  810. def get_policy_id_callback(res, policy_id):
  811. # In case of plugin init section: give it the id the policy plugin
  812. # will use
  813. if res.get('display_name') == \
  814. v3_plugin.NSX_V3_FW_DEFAULT_SECTION:
  815. return p_plugin.NSX_P_DEFAULT_SECTION
  816. return policy_id
  817. def add_metadata(entry, policy_id, resource):
  818. # Add category, sequence, domain, and rule ids
  819. ctx = context.get_admin_context()
  820. category = p_plugin.NSX_P_REGULAR_SECTION_CATEGORY
  821. if policy_id == p_plugin.NSX_P_DEFAULT_SECTION:
  822. category = p_plugin.NSX_P_DEFAULT_SECTION_CATEGORY
  823. else:
  824. sg = plugin.get_security_group(ctx, policy_id)
  825. provider = sg.get('provider')
  826. if provider:
  827. category = p_plugin.NSX_P_PROVIDER_SECTION_CATEGORY
  828. global DFW_SEQ
  829. metadata = [{'key': "category", 'value': category},
  830. {'key': "sequence", 'value': str(DFW_SEQ)}]
  831. DFW_SEQ = DFW_SEQ + 1
  832. # Add the rules
  833. rules = nsxlib.firewall_section.get_rules(resource['id'])['results']
  834. linked_ids = []
  835. seq = 1
  836. for rule in rules:
  837. linked_ids.append({'key': rule['id'], 'value': str(seq)})
  838. if policy_id == p_plugin.NSX_P_DEFAULT_SECTION:
  839. # Default section rule ids are their seq numbers
  840. linked_ids.append({'key': "%s-policyid" % rule['id'],
  841. 'value': seq})
  842. else:
  843. # The display name of the MP rule is the neutron id, and this
  844. # will become the policy id
  845. linked_ids.append({'key': "%s-policyid" % rule['id'],
  846. 'value': rule['display_name']})
  847. seq = seq + 1
  848. entry['metadata'] = metadata
  849. entry['linked_ids'] = linked_ids
  850. def get_policy_section(sec_id, silent=False):
  851. return nsxpolicy.comm_map.get(policy_constants.DEFAULT_DOMAIN, sec_id,
  852. silent=silent)
  853. entries = get_resource_migration_data(
  854. nsxlib.firewall_section,
  855. ['os-neutron-secgr-id', 'os-neutron-id'],
  856. 'DFW_SECTION', resource_condition=dfw_migration_cond,
  857. policy_resource_get=get_policy_section,
  858. policy_id_callback=get_policy_id_callback,
  859. metadata_callback=add_metadata)
  860. migrate_resource(nsxlib, 'DFW_SECTION', entries,
  861. MIGRATE_LIMIT_SECTION_AND_RULES,
  862. count_internals=True)
  863. def migrate_dhcp_servers(nsxlib, nsxpolicy):
  864. # Each MP DHCP server will be migrated to a policy DHCP server config
  865. # which will be used by a segment later. It will get the neutron network id
  866. entries = get_resource_migration_data(
  867. nsxlib.dhcp_server,
  868. ['os-neutron-net-id'],
  869. 'DHCP_SERVER',
  870. policy_resource_get=nsxpolicy.dhcp_server_config.get)
  871. migrate_resource(nsxlib, 'DHCP_SERVER', entries,
  872. MIGRATE_LIMIT_DHCP_SERVER)
  873. def migrate_lb_resources(nsxlib, nsxpolicy):
  874. migrate_lb_certificates(nsxlib, nsxpolicy)
  875. migrate_lb_monitors(nsxlib, nsxpolicy)
  876. migrate_lb_pools(nsxlib, nsxpolicy)
  877. migrate_lb_profiles(nsxlib, nsxpolicy)
  878. migrate_lb_listeners(nsxlib, nsxpolicy)
  879. migrate_lb_services(nsxlib, nsxpolicy)
  880. def migrate_lb_certificates(nsxlib, nsxpolicy):
  881. entries = get_resource_migration_data(
  882. nsxlib.trust_management,
  883. [lb_const.LB_LISTENER_TYPE],
  884. 'CERTIFICATE',
  885. policy_resource_get=nsxpolicy.certificate.get)
  886. migrate_resource(nsxlib, 'CERTIFICATE', entries,
  887. MIGRATE_LIMIT_CERT)
  888. def _migrate_lb_resource(nsxlib, nsxpolicy, neutron_tag, api_name,
  889. migration_name, limit,
  890. policy_api_name=None,
  891. policy_id_callback=None):
  892. if not policy_api_name:
  893. policy_api_name = api_name
  894. entries = get_resource_migration_data(
  895. getattr(nsxlib.load_balancer, api_name),
  896. [neutron_tag],
  897. migration_name,
  898. policy_resource_get=getattr(nsxpolicy.load_balancer,
  899. policy_api_name).get,
  900. policy_id_callback=policy_id_callback)
  901. migrate_resource(nsxlib, migration_name, entries, limit)
  902. def migrate_lb_listeners(nsxlib, nsxpolicy):
  903. _migrate_lb_resource(nsxlib, nsxpolicy,
  904. lb_const.LB_LISTENER_TYPE,
  905. 'virtual_server',
  906. 'LB_VIRTUAL_SERVER',
  907. MIGRATE_LIMIT_LB_VIRTUAL_SERVER)
  908. def migrate_lb_pools(nsxlib, nsxpolicy):
  909. _migrate_lb_resource(nsxlib, nsxpolicy,
  910. lb_const.LB_POOL_TYPE,
  911. 'pool',
  912. 'LB_POOL',
  913. MIGRATE_LIMIT_LB_POOL,
  914. policy_api_name='lb_pool')
  915. def migrate_lb_monitors(nsxlib, nsxpolicy):
  916. _migrate_lb_resource(nsxlib, nsxpolicy,
  917. lb_const.LB_HM_TYPE,
  918. 'monitor',
  919. 'LB_MONITOR',
  920. MIGRATE_LIMIT_LB_MONITOR,
  921. policy_api_name='lb_monitor_profile_http')
  922. def migrate_lb_profiles(nsxlib, nsxpolicy):
  923. _migrate_lb_resource(nsxlib, nsxpolicy,
  924. lb_const.LB_LISTENER_TYPE,
  925. 'application_profile',
  926. 'LB_APPLICATION_PROFILE',
  927. MIGRATE_LIMIT_LB_APP_PROFILE,
  928. policy_api_name='lb_http_profile')
  929. def get_policy_id_callback(res, policy_id):
  930. # The input policy id is the pool id
  931. # Need to add a suffix regarding the type of persistence
  932. if (res.get('resource_type') ==
  933. nsxlib_lb.PersistenceProfileTypes.SOURCE_IP):
  934. return "%s_%s" % (policy_id, 'sourceip')
  935. else:
  936. return "%s_%s" % (policy_id, 'cookie')
  937. _migrate_lb_resource(nsxlib, nsxpolicy,
  938. lb_const.LB_POOL_TYPE,
  939. 'persistence_profile',
  940. 'LB_PERSISTENCE_PROFILE',
  941. MIGRATE_LIMIT_LB_PER_PROFILE,
  942. policy_api_name='lb_persistence_profile',
  943. policy_id_callback=get_policy_id_callback)
  944. def migrate_lb_services(nsxlib, nsxpolicy):
  945. def get_policy_id_callback(res, policy_id):
  946. # LB service is shared between few octavia loadbalancers
  947. # so the policy id is not the LB id, and those should be marked
  948. # in the tags of the policy resource.
  949. # Keep the same id as MP so later we can search the MP DB
  950. # and update the tags
  951. return res['id']
  952. entries = get_resource_migration_data(
  953. nsxlib.load_balancer.service,
  954. ['os-api-version'],
  955. 'LB_SERVICE',
  956. policy_resource_get=nsxpolicy.load_balancer.lb_service.get,
  957. policy_id_callback=get_policy_id_callback)
  958. migrate_resource(nsxlib, 'LB_SERVICE', entries,
  959. MIGRATE_LIMIT_LB_SERVICE)
  960. def migrate_t_resources_2_p(nsxlib, nsxpolicy, plugin):
  961. """Create policy resources for all MP resources used by neutron"""
  962. # Initialize the migration process
  963. if not ensure_migration_state_ready(nsxlib, with_abort=True):
  964. return False
  965. try:
  966. LOG.info("Starting resources migration")
  967. start_migration_process(nsxlib)
  968. # Migration order derives from the dependencies between resources
  969. public_switches, tier0s = migrate_tier0s(nsxlib, nsxpolicy, plugin)
  970. migrate_md_proxies(nsxlib, nsxpolicy, plugin)
  971. migrate_switch_profiles(nsxlib, nsxpolicy, plugin)
  972. migrate_groups(nsxlib, nsxpolicy)
  973. migrate_dhcp_servers(nsxlib, nsxpolicy)
  974. mp_routers = migrate_routers(nsxlib, nsxpolicy)
  975. migrate_networks(nsxlib, nsxpolicy, plugin, public_switches)
  976. migrate_ports(nsxlib, nsxpolicy, plugin)
  977. migrate_routers_config(nsxlib, nsxpolicy, plugin, mp_routers)
  978. migrate_tier0_config(nsxlib, nsxpolicy, tier0s)
  979. migrate_lb_resources(nsxlib, nsxpolicy)
  980. # Migrate firewall sections last as those take the longest to rollback
  981. # in case of error
  982. migrate_dfw_sections(nsxlib, nsxpolicy, plugin)
  983. # Finalize the migration (cause policy realization)
  984. end_migration_process(nsxlib)
  985. # Stop the migration service
  986. change_migration_service_status(start=False)
  987. return True
  988. except Exception as e:
  989. # Migration failed - abort it
  990. LOG.error("Exception occurred while making the request: %s", e)
  991. try:
  992. LOG.info("Aborting the current request")
  993. try:
  994. send_migration_plan_action(nsxlib, 'abort')
  995. except Exception as e:
  996. LOG.error("Abort migration failed: %s", e)
  997. global ROLLBACK_DATA
  998. if ROLLBACK_DATA:
  999. LOG.info("Rolling migration back %s", ROLLBACK_DATA)
  1000. send_rollback_request(nsxlib,
  1001. {'migration_data': ROLLBACK_DATA})
  1002. # Finalize the migration (Also needed after rollback)
  1003. end_migration_process(nsxlib)
  1004. # Stop the migration service
  1005. change_migration_service_status(start=False)
  1006. except Exception as e:
  1007. LOG.error("Rollback failed: %s", e)
  1008. return False
  1009. def _get_network_nsx_segment_id(ctx, net_id):
  1010. bindings = db.get_network_bindings(ctx.session, net_id)
  1011. if (bindings and
  1012. bindings[0].binding_type ==
  1013. nsx_utils.NsxV3NetworkTypes.NSX_NETWORK):
  1014. # return the ID of the NSX network
  1015. return bindings[0].phy_uuid
  1016. return net_id
  1017. def _delete_segment_profiles_bindings(nsxpolicy, segment_id):
  1018. found = False
  1019. sec_profiles = nsxpolicy.segment_security_profile_maps.list(segment_id)
  1020. for profile in sec_profiles:
  1021. found = True
  1022. nsxpolicy.segment_security_profile_maps.delete(
  1023. segment_id, profile['id'])
  1024. qos_profiles = nsxpolicy.segment_qos_profile_maps.list(segment_id)
  1025. for profile in qos_profiles:
  1026. found = True
  1027. nsxpolicy.segment_qos_profile_maps.delete(
  1028. segment_id, profile['id'])
  1029. discovery_profiles = nsxpolicy.segment_discovery_profile_maps.list(
  1030. segment_id)
  1031. for profile in discovery_profiles:
  1032. found = True
  1033. nsxpolicy.segment_discovery_profile_maps.delete(
  1034. segment_id, profile['id'])
  1035. if found:
  1036. LOG.debug("Removed profiles mappings from segment %s", segment_id)
  1037. def post_migration_actions(nsxlib, nsxpolicy, nsxpolicy_admin, plugin):
  1038. """Update created policy resources that does not match the policy plugins'
  1039. expectations.
  1040. """
  1041. LOG.info("Starting post-migration actions")
  1042. ctx = context.get_admin_context()
  1043. # -- Update Lb tags on loadbalancer service
  1044. pol_lb_services = nsxpolicy.load_balancer.lb_service.list()
  1045. for lb_srv in pol_lb_services:
  1046. # Verify this is a neutron resource
  1047. if not is_neutron_resource(lb_srv):
  1048. continue
  1049. # Check if it already has the LB id tag
  1050. migrated = False
  1051. for tag in lb_srv.get('tags', []):
  1052. if tag['scope'] == lb_utils.SERVICE_LB_TAG_SCOPE:
  1053. migrated = True
  1054. break
  1055. if migrated:
  1056. continue
  1057. # Find the loadbalancers using this service from the DB
  1058. lb_mapping = db.get_nsx_lbaas_loadbalancer_binding_by_service(
  1059. ctx.session, lb_srv['id'])
  1060. if lb_mapping:
  1061. if 'tags' not in lb_srv:
  1062. lb_srv['tags'] = []
  1063. loadbalancers = [lb_map.loadbalancer_id for lb_map in lb_mapping]
  1064. for lb_id in loadbalancers:
  1065. lb_srv['tags'].append({'scope': lb_utils.SERVICE_LB_TAG_SCOPE,
  1066. 'tag': lb_id})
  1067. nsxpolicy.load_balancer.lb_service.update(
  1068. lb_srv['id'], tags=lb_srv['tags'])
  1069. LOG.debug("Added tags to LB service %s", lb_srv['id'])
  1070. # -- Update Lb L7 rules names
  1071. mp_lb_rules = nsxlib.load_balancer.rule.list()['results']
  1072. for mp_rule in mp_lb_rules:
  1073. l7pol_id = None
  1074. listener_id = None
  1075. for tag in mp_rule.get('tags', []):
  1076. if tag['scope'] == lb_const.LB_L7POLICY_TYPE:
  1077. l7pol_id = tag['tag']
  1078. if tag['scope'] == 'policyPath':
  1079. listener_id = policy_utils.path_to_id(tag['tag'])
  1080. if not l7pol_id or not listener_id:
  1081. continue
  1082. pol_vs = nsxpolicy.load_balancer.virtual_server.get(listener_id)
  1083. pol_rules = pol_vs['rules']
  1084. for pol_rule in pol_rules:
  1085. if pol_rule['display_name'] == mp_rule['id']:
  1086. new_name = nsx_utils.get_name_and_uuid('policy', l7pol_id)
  1087. pol_rule['display_name'] = new_name
  1088. nsxpolicy.load_balancer.virtual_server.update_lb_rules(
  1089. listener_id, pol_rules)
  1090. LOG.debug("Updated L7 policy %s name on the virtual server",
  1091. l7pol_id)
  1092. break
  1093. # -- Create DHCP server configs to be used in neutron config
  1094. # (The migration does not migrate MP DHCP profiles)
  1095. neutron_dhcp = get_configured_values(plugin, '_native_dhcp_profile_uuid')
  1096. for mp_dhcp in neutron_dhcp:
  1097. # check if it was already migrated
  1098. try:
  1099. nsxpolicy.dhcp_server_config.get(mp_dhcp, silent=True)
  1100. except Exception:
  1101. # Create it
  1102. mp_obj = nsxlib.native_dhcp_profile.get(mp_dhcp)
  1103. # This should be created with the admin principal identity
  1104. nsxpolicy_admin.dhcp_server_config.create_or_overwrite(
  1105. mp_obj['display_name'],
  1106. config_id=mp_dhcp,
  1107. description=mp_obj.get('description', ''),
  1108. edge_cluster_path=nsxpolicy.edge_cluster.get_path(
  1109. mp_obj['edge_cluster_id']))
  1110. LOG.debug("Created DHCP server config %s for plugin config",
  1111. mp_dhcp)
  1112. # -- Update Policy segments:
  1113. # Set subnets GW for networks without linked routers
  1114. # And remove unused segment profiles mappings
  1115. networks = plugin.get_networks(ctx)
  1116. for net in networks:
  1117. if net.get('router:external'):
  1118. continue
  1119. seg_id = _get_network_nsx_segment_id(ctx, net['id'])
  1120. if seg_id == net['id']:
  1121. # This is not an nsx-net. Delete the bindings
  1122. _delete_segment_profiles_bindings(nsxpolicy, seg_id)
  1123. if plugin._get_network_router_ids(ctx, net['id']):
  1124. continue
  1125. # verify that this network has a dhcp subnet
  1126. subnets = plugin.get_subnets_by_network(ctx, net['id'])
  1127. for subnet in subnets:
  1128. if subnet['ip_version'] == 4 and subnet['enable_dhcp']:
  1129. # Update backend subnet
  1130. segment = nsxpolicy.segment.get(seg_id)
  1131. subnets = segment.get('subnets', [])
  1132. if subnets and len(subnets) == 1 and subnet['gateway_ip']:
  1133. cidr_prefix = int(subnet['cidr'].split('/')[1])
  1134. gw = "%s/%s" % (subnet['gateway_ip'], cidr_prefix)
  1135. subnets[0]['gateway_address'] = gw
  1136. nsxpolicy.segment.update(seg_id, subnets=subnets)
  1137. LOG.debug("Updated gateway of network %s", net['id'])
  1138. break
  1139. # -- Migrate edge firewall sections:
  1140. # The MP plugin uses the default MP edge firewall section, while the policy
  1141. # plugin uses a non default one, so regular migration cannot be used.
  1142. # Instead, create new edge firewall sections, and remove rules from the MP
  1143. # default sections
  1144. # This is a hack to use the v3 plugin with the policy fwaas driver
  1145. class MigrationNsxpFwaasCallbacks(fwaas_callbacks_v2.NsxpFwaasCallbacksV2):
  1146. def __init__(self, with_rpc):
  1147. super(MigrationNsxpFwaasCallbacks, self).__init__(with_rpc)
  1148. # Make sure fwaas is considered as enabled
  1149. self.fwaas_enabled = True
  1150. def _get_port_firewall_group_id(self, context, port_id):
  1151. # Override this api because directory.get_plugin does not work from
  1152. # admin utils context.
  1153. driver_db = firewall_db_v2.FirewallPluginDb()
  1154. return driver_db.get_fwg_attached_to_port(context, port_id)
  1155. fwaas_callbacks = MigrationNsxpFwaasCallbacks(False)
  1156. plugin.nsxpolicy = nsxpolicy
  1157. routers = plugin.get_routers(ctx)
  1158. nsx_router_sections = []
  1159. for rtr in routers:
  1160. nsx_router_id = db.get_nsx_router_id(ctx.session, rtr['id'])
  1161. nsx_rtr = nsxlib.logical_router.get(nsx_router_id)
  1162. for sec in nsx_rtr.get('firewall_sections', []):
  1163. section_id = sec['target_id']
  1164. section = nsxlib.firewall_section.get(section_id)
  1165. if section['display_name'] != 'Default LR Layer3 Section':
  1166. continue
  1167. rules = nsxlib.firewall_section.get_rules(section_id)['results']
  1168. if len(rules) <= 1:
  1169. continue
  1170. # Non default rules exist. need to migrate this section
  1171. router_db = plugin._get_router(ctx, rtr['id'])
  1172. ports = plugin._get_router_interfaces(ctx, rtr['id'])
  1173. fwaas_callbacks.update_router_firewall(
  1174. ctx, rtr['id'], router_db, ports)
  1175. LOG.debug("Created GW policy for router %s", rtr['id'])
  1176. # delete rule from the default mp section at the end of the loop
  1177. # so the new section will have time to realize
  1178. nsx_router_sections.append({'id': section_id,
  1179. 'default_rule': rules[-1],
  1180. 'router_id': rtr['id']})
  1181. # Remove old rules from the default sections
  1182. for section in nsx_router_sections:
  1183. # make sure the policy section was already realized
  1184. # with runtime_status=SUCESS
  1185. nsxpolicy.gateway_policy.wait_until_state_sucessful(
  1186. policy_constants.DEFAULT_DOMAIN, section['router_id'],
  1187. max_attempts=600, with_refresh=True)
  1188. nsxlib.firewall_section.update(
  1189. section['id'], rules=[section['default_rule']])
  1190. LOG.debug("Deleted MP edge FW section %s rules", section['id'])
  1191. LOG.info("Post-migration actions done.")
  1192. def edge_firewall_migration_cond(resource):
  1193. return (resource.get('display_name') == 'Default LR Layer3 Section' and
  1194. resource.get('enforced_on') == 'LOGICALROUTER' and
  1195. resource.get('category') == 'Default' and
  1196. resource.get('section_type') == 'LAYER3')
  1197. def pre_migration_checks(nsxlib, plugin):
  1198. """Check for unsupported configuration that will fail the migration"""
  1199. nsx_version = nsxlib.get_version()
  1200. if not nsx_utils.is_nsx_version_3_1_0(nsx_version):
  1201. LOG.error("Pre migration check failed: Migration not supported for "
  1202. "NSX %s", nsx_version)
  1203. return False
  1204. # Cannot migrate with unsupported services
  1205. service_plugins = cfg.CONF.service_plugins
  1206. for srv_plugin in service_plugins:
  1207. if 'vpnaas' in srv_plugin:
  1208. LOG.error("Pre migration check failed: VPNaaS is not supported. "
  1209. "Please delete its configuration and disable it, before "
  1210. "running migration again.")
  1211. return False
  1212. if 'l2gw' in srv_plugin:
  1213. LOG.error("Pre migration check failed: L2GW is not supported. "
  1214. "Please delete its configuration and disable it, before "
  1215. "running migration again.")
  1216. return False
  1217. # Tier0 with disabled BGP config
  1218. neutron_t0s = get_neurton_tier0s(plugin)
  1219. for tier0 in neutron_t0s:
  1220. bgp_conf = nsxlib.logical_router.get_bgp_config(tier0)
  1221. if not bgp_conf['enabled']:
  1222. # Verify there are no neighbors configured
  1223. if nsxlib.logical_router.get_bgp_neighbors(tier0)['result_count']:
  1224. LOG.error("Pre migration check failed: Tier0 %s has BGP "
  1225. "neighbors but BGP is disabled. Please remove the "
  1226. "neighbors or enable BGP and try again.", tier0)
  1227. return False
  1228. # Firewall section with too many rules
  1229. fw_sections = nsxlib.firewall_section.list()
  1230. for section in fw_sections:
  1231. if (dfw_migration_cond(section) or
  1232. edge_firewall_migration_cond(section)):
  1233. n_rules = nsxlib.firewall_section.get_rules(
  1234. section['id'])['result_count']
  1235. if n_rules >= MIGRATE_LIMIT_SECTION_AND_RULES:
  1236. LOG.error("Pre migration check failed: Firewall section %s "
  1237. "has %s rules and cannot be migrated. Please make "
  1238. "sure each section has less than %s rules and try "
  1239. "again.", section['id'], n_rules,
  1240. MIGRATE_LIMIT_SECTION_AND_RULES)
  1241. return False
  1242. # DHCP relay is unsupported
  1243. if plugin._availability_zones_data.dhcp_relay_configured():
  1244. LOG.error("Pre migration check failed: DHCP relay configuration "
  1245. "cannot be migrated. Please remove it from the plugin "
  1246. "configuration and from all NSX logical router ports and "
  1247. "try again.")
  1248. return False
  1249. return True
  1250. @admin_utils.output_header
  1251. def MP2Policy_pre_migration_check(resource, event, trigger, **kwargs):
  1252. """Verify if the current configuration can be migrated to Policy"""
  1253. nsxlib = utils.get_connected_nsxlib()
  1254. with utils.NsxV3PluginWrapper() as plugin:
  1255. if not pre_migration_checks(nsxlib, plugin):
  1256. # Failed
  1257. LOG.error("T2P migration cannot run. Please fix the configuration "
  1258. "and try again\n\n")
  1259. exit(1)
  1260. def _get_nsxlib_from_config(verbose):
  1261. """Update the current config and return a working nsxlib
  1262. or exit with error
  1263. """
  1264. if (not len(cfg.CONF.nsx_v3.nsx_api_user) or
  1265. not len(cfg.CONF.nsx_v3.nsx_api_password)):
  1266. LOG.error("T2P migration cannot run. Please provide nsx_api_user and "
  1267. "nsx_api_password in the configuration.")
  1268. exit(1)
  1269. retriables = [nsxlib_exc.APITransactionAborted,
  1270. nsxlib_exc.ServerBusy]
  1271. # Initialize the nsxlib objects, using just one of the managers because
  1272. # the migration will be enabled only on one
  1273. nsx_api_managers = copy.copy(cfg.CONF.nsx_v3.nsx_api_managers)
  1274. nsx_api_user = copy.copy(cfg.CONF.nsx_v3.nsx_api_user)
  1275. nsx_api_password = copy.copy(cfg.CONF.nsx_v3.nsx_api_password)
  1276. for ind in range(len(nsx_api_managers)):
  1277. # update the config to use this one manager only
  1278. cfg.CONF.set_override(
  1279. 'nsx_api_managers', [nsx_api_managers[ind]], 'nsx_v3')
  1280. if len(nsx_api_user) > ind:
  1281. cfg.CONF.set_override(
  1282. 'nsx_api_user', [nsx_api_user[ind]], 'nsx_v3')
  1283. else:
  1284. cfg.CONF.set_override(
  1285. 'nsx_api_user', [nsx_api_user[0]], 'nsx_v3')
  1286. if len(nsx_api_password) > ind:
  1287. cfg.CONF.set_override(
  1288. 'nsx_api_password', [nsx_api_password[ind]], 'nsx_v3')
  1289. else:
  1290. cfg.CONF.set_override(
  1291. 'nsx_api_password', [nsx_api_password[0]], 'nsx_v3')
  1292. utils.reset_global_nsxlib()
  1293. nsxlib = utils.get_connected_nsxlib(verbose=verbose,
  1294. allow_overwrite_header=True,
  1295. retriable_exceptions=retriables)
  1296. try:
  1297. # test connectivity
  1298. nsxlib.get_version()
  1299. except Exception:
  1300. LOG.warning("Failed to connect to NSX manager %s",
  1301. nsx_api_managers[ind])
  1302. else:
  1303. # Found a working manager
  1304. return nsxlib
  1305. LOG.error("T2P migration failed. Cannot connect to NSX with managers %s",
  1306. nsx_api_managers)
  1307. exit(1)
  1308. @admin_utils.output_header
  1309. def MP2Policy_migration(resource, event, trigger, **kwargs):
  1310. """Migrate NSX resources and neutron DB from NSX-T (MP) to Policy"""
  1311. verbose = kwargs.get('verbose', False)
  1312. if verbose:
  1313. # Add DEBUG logs as well
  1314. LOG.setLevel(logging.DEBUG)
  1315. else:
  1316. LOG.setLevel(logging.INFO)
  1317. if kwargs.get('property'):
  1318. # Add logfile
  1319. properties = admin_utils.parse_multi_keyval_opt(kwargs['property'])
  1320. logfile = properties.get('logfile', None)
  1321. if logfile:
  1322. f_handler = logging.FileHandler(logfile)
  1323. f_formatter = logging.Formatter(
  1324. '%(asctime)s %(levelname)s %(message)s')
  1325. f_handler.setFormatter(f_formatter)
  1326. LOG.addHandler(f_handler)
  1327. nsxlib = _get_nsxlib_from_config(verbose)
  1328. nsxpolicy = p_utils.get_connected_nsxpolicy(
  1329. conf_path=cfg.CONF.nsx_v3, verbose=verbose)
  1330. if cfg.CONF.nsx_v3.nsx_use_client_auth:
  1331. # Also create a policy manager with admin user to manipulate
  1332. # admin-defined resources which should not have neutron principal
  1333. # identity
  1334. nsxpolicy_admin = p_utils.get_connected_nsxpolicy(
  1335. conf_path=cfg.CONF.nsx_v3,
  1336. use_basic_auth=True,
  1337. nsx_username=cfg.CONF.nsx_v3.nsx_api_user,
  1338. nsx_password=cfg.CONF.nsx_v3.nsx_api_password,
  1339. verbose=verbose)
  1340. else:
  1341. nsxpolicy_admin = nsxpolicy
  1342. with utils.NsxV3PluginWrapper(verbose=verbose) as plugin:
  1343. # Make sure FWaaS was initialized
  1344. plugin.init_fwaas_for_admin_utils()
  1345. start_time = time.time()
  1346. if not pre_migration_checks(nsxlib, plugin):
  1347. # Failed
  1348. LOG.error("T2P migration cannot run. Please fix the configuration "
  1349. "and try again\n\n")
  1350. exit(1)
  1351. elapsed_time = time.time() - start_time
  1352. LOG.debug("Pre-migration took %s seconds", elapsed_time)
  1353. start_time = time.time()
  1354. if not migrate_t_resources_2_p(nsxlib, nsxpolicy, plugin):
  1355. # Failed
  1356. LOG.error("T2P migration failed. Aborting\n\n")
  1357. exit(1)
  1358. elapsed_time = time.time() - start_time
  1359. LOG.debug("Migration took %s seconds", elapsed_time)
  1360. start_time = time.time()
  1361. post_migration_actions(nsxlib, nsxpolicy, nsxpolicy_admin, plugin)
  1362. elapsed_time = time.time() - start_time
  1363. LOG.debug("Post-migration took %s seconds", elapsed_time)
  1364. LOG.info("T2P migration completed successfully\n\n")
  1365. @admin_utils.output_header
  1366. def MP2Policy_cleanup_db_mappings(resource, event, trigger, **kwargs):
  1367. """Delete all entries from nsx-t mapping tables in DB"""
  1368. confirm = admin_utils.query_yes_no(
  1369. "Are you sure you want to delete all MP plugin mapping DB tables?",
  1370. default="no")
  1371. if not confirm:
  1372. LOG.info("Deletion aborted by user")
  1373. return
  1374. ctx = context.get_admin_context()
  1375. mp_mapping_tables = [nsx_models.NeutronNsxFirewallSectionMapping,
  1376. nsx_models.NeutronNsxSecurityGroupMapping,
  1377. nsx_models.NeutronNsxRuleMapping,
  1378. nsx_models.NeutronNsxPortMapping,
  1379. nsx_models.NeutronNsxRouterMapping,
  1380. nsx_models.NeutronNsxServiceBinding,
  1381. nsx_models.NeutronNsxDhcpBinding,
  1382. nsx_models.QosPolicySwitchProfile,
  1383. nsx_models.NsxLbaasLoadbalancer,
  1384. nsx_models.NsxLbaasListener,
  1385. nsx_models.NsxLbaasPool,
  1386. nsx_models.NsxLbaasMonitor,
  1387. nsx_models.NsxLbaasL7Rule,
  1388. nsx_models.NsxLbaasL7Policy]
  1389. for table in mp_mapping_tables:
  1390. ctx.session.query(table).delete()
  1391. LOG.info("Deleted all MP plugin mapping DB tables.")
  1392. registry.subscribe(MP2Policy_migration,
  1393. constants.NSX_MIGRATE_T_P,
  1394. shell.Operations.IMPORT.value)
  1395. registry.subscribe(MP2Policy_pre_migration_check,
  1396. constants.NSX_MIGRATE_T_P,
  1397. shell.Operations.VALIDATE.value)
  1398. registry.subscribe(MP2Policy_cleanup_db_mappings,
  1399. constants.NSX_MIGRATE_T_P,
  1400. shell.Operations.CLEAN_ALL.value)