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.

3159 lines
141KB

  1. # Copyright 2018 VMware, Inc.
  2. # All Rights Reserved
  3. #
  4. # Licensed under the Apache License, Version 2.0 (the "License"); you may
  5. # not use this file except in compliance with the License. You may obtain
  6. # a copy of the License at
  7. #
  8. # http://www.apache.org/licenses/LICENSE-2.0
  9. #
  10. # Unless required by applicable law or agreed to in writing, software
  11. # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
  12. # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
  13. # License for the specific language governing permissions and limitations
  14. # under the License.
  15. import time
  16. import netaddr
  17. from oslo_config import cfg
  18. from oslo_db import exception as db_exc
  19. from oslo_log import log
  20. from oslo_utils import excutils
  21. from oslo_utils import uuidutils
  22. from neutron.db import agents_db
  23. from neutron.db import l3_db
  24. from neutron.db.models import l3 as l3_db_models
  25. from neutron.db.models import securitygroup as securitygroup_model
  26. from neutron.db import models_v2
  27. from neutron.extensions import securitygroup as ext_sg
  28. from neutron.quota import resource_registry
  29. from neutron_lib.api.definitions import address_scope
  30. from neutron_lib.api.definitions import agent as agent_apidef
  31. from neutron_lib.api.definitions import allowedaddresspairs as addr_apidef
  32. from neutron_lib.api.definitions import availability_zone as az_apidef
  33. from neutron_lib.api.definitions import dhcpagentscheduler
  34. from neutron_lib.api.definitions import external_net
  35. from neutron_lib.api.definitions import extra_dhcp_opt as ext_edo
  36. from neutron_lib.api.definitions import extraroute
  37. from neutron_lib.api.definitions import l3 as l3_apidef
  38. from neutron_lib.api.definitions import network_availability_zone
  39. from neutron_lib.api.definitions import port_security as psec
  40. from neutron_lib.api.definitions import portbindings as pbin_apidef
  41. from neutron_lib.api.definitions import provider_net as pnet_apidef
  42. from neutron_lib.api.definitions import router_availability_zone
  43. from neutron_lib.api.definitions import vlantransparent as vlan_apidef
  44. from neutron_lib.api import extensions
  45. from neutron_lib.api import validators
  46. from neutron_lib.callbacks import events
  47. from neutron_lib.callbacks import registry
  48. from neutron_lib.callbacks import resources
  49. from neutron_lib import constants as const
  50. from neutron_lib import context as n_context
  51. from neutron_lib.db import api as db_api
  52. from neutron_lib.db import resource_extend
  53. from neutron_lib.db import utils as db_utils
  54. from neutron_lib import exceptions as n_exc
  55. from neutron_lib.plugins import constants as plugin_const
  56. from neutron_lib.plugins import directory
  57. from neutron_lib.services.qos import constants as qos_consts
  58. from vmware_nsx._i18n import _
  59. from vmware_nsx.common import config # noqa
  60. from vmware_nsx.common import exceptions as nsx_exc
  61. from vmware_nsx.common import l3_rpc_agent_api
  62. from vmware_nsx.common import locking
  63. from vmware_nsx.common import managers
  64. from vmware_nsx.common import utils
  65. from vmware_nsx.db import db as nsx_db
  66. from vmware_nsx.extensions import api_replay
  67. from vmware_nsx.extensions import maclearning as mac_ext
  68. from vmware_nsx.extensions import projectpluginmap
  69. from vmware_nsx.extensions import providersecuritygroup as provider_sg
  70. from vmware_nsx.extensions import secgroup_rule_local_ip_prefix as sg_prefix
  71. from vmware_nsx.extensions import securitygrouplogging as sg_logging
  72. from vmware_nsx.plugins.common_v3 import plugin as nsx_plugin_common
  73. from vmware_nsx.plugins.nsx_p import availability_zones as nsxp_az
  74. from vmware_nsx.plugins.nsx_v3 import utils as v3_utils
  75. from vmware_nsx.services.fwaas.common import utils as fwaas_utils
  76. from vmware_nsx.services.fwaas.nsx_p import fwaas_callbacks_v2
  77. from vmware_nsx.services.lbaas import lb_const
  78. from vmware_nsx.services.lbaas.nsx_p.implementation import healthmonitor_mgr
  79. from vmware_nsx.services.lbaas.nsx_p.implementation import l7policy_mgr
  80. from vmware_nsx.services.lbaas.nsx_p.implementation import l7rule_mgr
  81. from vmware_nsx.services.lbaas.nsx_p.implementation import listener_mgr
  82. from vmware_nsx.services.lbaas.nsx_p.implementation import loadbalancer_mgr
  83. from vmware_nsx.services.lbaas.nsx_p.implementation import member_mgr
  84. from vmware_nsx.services.lbaas.nsx_p.implementation import pool_mgr
  85. from vmware_nsx.services.lbaas.octavia import constants as oct_const
  86. from vmware_nsx.services.lbaas.octavia import octavia_listener
  87. from vmware_nsx.services.qos.common import utils as qos_com_utils
  88. from vmware_nsx.services.qos.nsx_v3 import driver as qos_driver
  89. from vmware_nsx.services.qos.nsx_v3 import pol_utils as qos_utils
  90. from vmware_nsx.services.trunk.nsx_p import driver as trunk_driver
  91. from vmware_nsxlib.v3 import exceptions as nsx_lib_exc
  92. from vmware_nsxlib.v3 import nsx_constants as nsxlib_consts
  93. from vmware_nsxlib.v3.policy import constants as policy_constants
  94. from vmware_nsxlib.v3.policy import core_defs as policy_defs
  95. from vmware_nsxlib.v3.policy import transaction as policy_trans
  96. from vmware_nsxlib.v3.policy import utils as p_utils
  97. from vmware_nsxlib.v3 import security
  98. from vmware_nsxlib.v3 import utils as nsxlib_utils
  99. LOG = log.getLogger(__name__)
  100. NSX_P_SECURITY_GROUP_TAG = 'os-security-group'
  101. NSX_P_GLOBAL_DOMAIN_ID = policy_constants.DEFAULT_DOMAIN
  102. NSX_P_DEFAULT_GROUP = 'os_default_group'
  103. NSX_P_DEFAULT_GROUP_DESC = 'Default Group for the openstack plugin'
  104. NSX_P_DEFAULT_SECTION = 'os_default_section'
  105. NSX_P_DEFAULT_SECTION_DESC = ('This section is handled by OpenStack to '
  106. 'contain default rules on security-groups.')
  107. NSX_P_DEFAULT_SECTION_CATEGORY = policy_constants.CATEGORY_APPLICATION
  108. NSX_P_REGULAR_SECTION_CATEGORY = policy_constants.CATEGORY_ENVIRONMENT
  109. NSX_P_PROVIDER_SECTION_CATEGORY = policy_constants.CATEGORY_INFRASTRUCTURE
  110. NSX_P_PORT_RESOURCE_TYPE = 'os-neutron-port-id'
  111. NSX_P_EXCLUDE_LIST_GROUP = 'neutron_excluded_ports_group'
  112. NSX_P_EXCLUDE_LIST_TAG = 'Exclude-Port'
  113. SPOOFGUARD_PROFILE_ID = 'neutron-spoofguard-profile'
  114. NO_SPOOFGUARD_PROFILE_ID = policy_defs.SpoofguardProfileDef.DEFAULT_PROFILE
  115. MAC_DISCOVERY_PROFILE_ID = 'neutron-mac-discovery-profile'
  116. NO_MAC_DISCOVERY_PROFILE_ID = (
  117. policy_defs.MacDiscoveryProfileDef.DEFAULT_PROFILE)
  118. NO_SEG_SECURITY_PROFILE_ID = 'neutron-no-segment-security-profile'
  119. SEG_SECURITY_PROFILE_ID = (
  120. policy_defs.SegmentSecurityProfileDef.DEFAULT_PROFILE)
  121. SLAAC_NDRA_PROFILE_ID = 'neutron-slaac-profile'
  122. NO_SLAAC_NDRA_PROFILE_ID = 'neutron-no-slaac-profile'
  123. IPV6_RA_SERVICE = 'neutron-ipv6-ra'
  124. IPV6_ROUTER_ADV_RULE_NAME = 'all-ipv6'
  125. # Priorities for NAT rules: (FIP specific rules should come before GW rules)
  126. NAT_RULE_PRIORITY_FIP = 2000
  127. NAT_RULE_PRIORITY_GW = 3000
  128. NSX_P_CLIENT_SSL_PROFILE = 'neutron-client-ssl-profile'
  129. # Cache for mapping between network ids in neutron and NSX (MP)
  130. NET_NEUTRON_2_NSX_ID_CACHE = {}
  131. NET_NSX_2_NEUTRON_ID_CACHE = {}
  132. @resource_extend.has_resource_extenders
  133. class NsxPolicyPlugin(nsx_plugin_common.NsxPluginV3Base):
  134. __native_bulk_support = True
  135. __native_pagination_support = True
  136. __native_sorting_support = True
  137. supported_extension_aliases = [addr_apidef.ALIAS,
  138. address_scope.ALIAS,
  139. "quotas",
  140. pbin_apidef.ALIAS,
  141. ext_edo.ALIAS,
  142. agent_apidef.ALIAS,
  143. dhcpagentscheduler.ALIAS,
  144. "ext-gw-mode",
  145. "security-group",
  146. sg_prefix.ALIAS,
  147. psec.ALIAS,
  148. pnet_apidef.ALIAS,
  149. external_net.ALIAS,
  150. extraroute.ALIAS,
  151. l3_apidef.ALIAS,
  152. az_apidef.ALIAS,
  153. network_availability_zone.ALIAS,
  154. router_availability_zone.ALIAS,
  155. "subnet_allocation",
  156. sg_logging.ALIAS,
  157. provider_sg.ALIAS,
  158. "port-security-groups-filtering",
  159. mac_ext.ALIAS,
  160. "advanced-service-providers"]
  161. @resource_registry.tracked_resources(
  162. network=models_v2.Network,
  163. port=models_v2.Port,
  164. subnet=models_v2.Subnet,
  165. subnetpool=models_v2.SubnetPool,
  166. security_group=securitygroup_model.SecurityGroup,
  167. security_group_rule=securitygroup_model.SecurityGroupRule,
  168. router=l3_db_models.Router,
  169. floatingip=l3_db_models.FloatingIP)
  170. def __init__(self):
  171. self.fwaas_callbacks = None
  172. self.init_is_complete = False
  173. self._is_sub_plugin = False
  174. self.octavia_listener = None
  175. self.octavia_stats_collector = None
  176. nsxlib_utils.set_is_attr_callback(validators.is_attr_set)
  177. self._extend_fault_map()
  178. extension_drivers = cfg.CONF.nsx_extension_drivers
  179. self._extension_manager = managers.ExtensionManager(
  180. extension_drivers=extension_drivers)
  181. self.cfg_group = 'nsx_p' # group name for nsx_p section in nsx.ini
  182. self.init_availability_zones()
  183. self.nsxpolicy = v3_utils.get_nsxpolicy_wrapper()
  184. # NOTE: This is needed for passthrough APIs, should be removed when
  185. # policy has full support
  186. self.nsxlib = None
  187. if cfg.CONF.nsx_p.allow_passthrough:
  188. self.nsxlib = v3_utils.get_nsxlib_wrapper(
  189. plugin_conf=cfg.CONF.nsx_p,
  190. allow_overwrite_header=True)
  191. super(NsxPolicyPlugin, self).__init__()
  192. # Bind the dummy L3 notifications
  193. self.l3_rpc_notifier = l3_rpc_agent_api.L3NotifyAPI()
  194. LOG.info("Starting NsxPolicyPlugin")
  195. self._extension_manager.initialize()
  196. self.supported_extension_aliases.extend(
  197. self._extension_manager.extension_aliases())
  198. # Support transparent VLANS only if the global configuration flag
  199. # vlan_transparent is True
  200. if cfg.CONF.vlan_transparent:
  201. self.supported_extension_aliases.append(vlan_apidef.ALIAS)
  202. # Support api-reply for migration environments to the policy plugin
  203. if cfg.CONF.api_replay_mode:
  204. self.supported_extension_aliases.append(api_replay.ALIAS)
  205. nsxlib_utils.set_inject_headers_callback(v3_utils.inject_headers)
  206. self._validate_nsx_policy_version()
  207. self._validate_config()
  208. self._init_default_config()
  209. self._prepare_default_rules()
  210. self._init_profiles()
  211. self._prepare_exclude_list()
  212. self._init_dhcp_metadata()
  213. # Init QoS
  214. qos_driver.register(qos_utils.PolicyQosNotificationsHandler())
  215. # Register NSXP trunk driver to support trunk extensions
  216. self.trunk_driver = trunk_driver.NsxpTrunkDriver.create(self)
  217. registry.subscribe(self.spawn_complete,
  218. resources.PROCESS,
  219. events.AFTER_SPAWN)
  220. # subscribe the init complete method last, so it will be called only
  221. # if init was successful
  222. registry.subscribe(self.init_complete,
  223. resources.PROCESS,
  224. events.AFTER_INIT)
  225. def _validate_config(self):
  226. if cfg.CONF.ipam_driver != 'internal':
  227. msg = _("External IPAM drivers not supported with nsxp plugin")
  228. LOG.error(msg)
  229. raise n_exc.InvalidInput(error_message=msg)
  230. def _init_default_config(self):
  231. # Ipv6 is disabled by default in NSX
  232. if cfg.CONF.nsx_p.allow_passthrough:
  233. self.nsxlib.global_routing.enable_ipv6()
  234. else:
  235. LOG.warning("Unable to switch on Ipv6 forwarding. Ipv6 "
  236. "connectivity might be broken.")
  237. # Default tier0/transport zones are initialized via the default AZ
  238. # Validate other mandatory configuration
  239. if cfg.CONF.nsx_p.allow_passthrough:
  240. if not cfg.CONF.nsx_p.dhcp_profile:
  241. raise cfg.RequiredOptError("dhcp_profile",
  242. group=cfg.OptGroup('nsx_p'))
  243. if not cfg.CONF.nsx_p.metadata_proxy:
  244. raise cfg.RequiredOptError("metadata_proxy",
  245. group=cfg.OptGroup('nsx_p'))
  246. # If using tags to find the objects, make sure tag scope is configured
  247. if (cfg.CONF.nsx_p.init_objects_by_tags and
  248. not cfg.CONF.nsx_p.search_objects_scope):
  249. raise cfg.RequiredOptError("search_objects_scope",
  250. group=cfg.OptGroup('nsx_p'))
  251. # Init AZ resources
  252. search_scope = (cfg.CONF.nsx_p.search_objects_scope
  253. if cfg.CONF.nsx_p.init_objects_by_tags else None)
  254. for az in self.get_azs_list():
  255. az.translate_configured_names_to_uuids(
  256. self.nsxpolicy, nsxlib=self.nsxlib, search_scope=search_scope)
  257. az.validate_availability_zone(self.nsxpolicy, nsxlib=self.nsxlib)
  258. # WAF is currently not supported by the NSX
  259. self._waf_profile_uuid = None
  260. try:
  261. self.nsxpolicy.mixed_service.get(IPV6_RA_SERVICE)
  262. except nsx_lib_exc.ResourceNotFound:
  263. # create or override ipv6 RA service
  264. unicast_ra = self.nsxpolicy.icmp_service.build_entry(
  265. 'unicast RA', IPV6_RA_SERVICE, 'unicast',
  266. version=6, icmp_type=134)
  267. multicast_ra = self.nsxpolicy.icmp_service.build_entry(
  268. 'multicast RA', IPV6_RA_SERVICE, 'multicast',
  269. version=6, icmp_type=151)
  270. try:
  271. self.nsxpolicy.mixed_service.create_or_overwrite(
  272. IPV6_RA_SERVICE, IPV6_RA_SERVICE,
  273. entries=[unicast_ra, multicast_ra])
  274. except nsx_lib_exc.StaleRevision as e:
  275. # This means that another controller is also creating this
  276. LOG.info("Failed to configure mixed_service: %s", e)
  277. except nsx_lib_exc.ManagerError:
  278. msg = _("Failed to configure RA service for IPv6 connectivity")
  279. LOG.error(msg)
  280. raise nsx_exc.NsxPluginException(err_msg=msg)
  281. def _init_backend_resource(self, resource_api, name_or_id,
  282. search_scope=None):
  283. resource_type = resource_api.entry_def.resource_type()
  284. if not name_or_id:
  285. return None
  286. try:
  287. # Check if the configured value is the ID
  288. resource_api.get(name_or_id, silent=True)
  289. return name_or_id
  290. except nsx_lib_exc.ResourceNotFound:
  291. # Search by tags
  292. if search_scope:
  293. resource_id = self.nsxpolicy.get_id_by_resource_and_tag(
  294. resource_type,
  295. search_scope,
  296. name_or_id)
  297. if resource_id:
  298. return resource_id
  299. # Check if the configured value is the name
  300. resource = resource_api.get_by_name(name_or_id)
  301. if resource:
  302. return resource['id']
  303. msg = (_("Could not find %(type)s %(id)s") % {
  304. 'type': resource_type, 'id': name_or_id})
  305. raise nsx_exc.NsxPluginException(err_msg=msg)
  306. def get_waf_profile_path_and_mode(self):
  307. # WAF is currently not supported by the NSX
  308. return None, None
  309. def _init_dhcp_metadata(self):
  310. if (cfg.CONF.dhcp_agent_notification and
  311. cfg.CONF.nsx_p.allow_passthrough):
  312. msg = _("Need to disable dhcp_agent_notification when "
  313. "native DHCP & Metadata is enabled")
  314. raise nsx_exc.NsxPluginException(err_msg=msg)
  315. self._init_native_dhcp()
  316. self._init_native_metadata()
  317. def init_availability_zones(self):
  318. self._availability_zones_data = nsxp_az.NsxPAvailabilityZones()
  319. def _validate_nsx_policy_version(self):
  320. self._nsx_version = self.nsxpolicy.get_version()
  321. LOG.info("NSX Version: %s", self._nsx_version)
  322. if (not self.nsxpolicy.feature_supported(
  323. nsxlib_consts.FEATURE_NSX_POLICY_NETWORKING) or
  324. not utils.is_nsx_version_2_5_0(self._nsx_version)):
  325. msg = (_("The NSX Policy plugin requires version 2.5 "
  326. "(current version %(ver)s)") % {'ver': self._nsx_version})
  327. raise nsx_exc.NsxPluginException(err_msg=msg)
  328. def _init_profiles(self):
  329. """Find/Create segment profiles this plugin will use"""
  330. # Spoofguard profile (find it or create)
  331. try:
  332. self.nsxpolicy.spoofguard_profile.get(SPOOFGUARD_PROFILE_ID)
  333. except nsx_lib_exc.ResourceNotFound:
  334. try:
  335. self.nsxpolicy.spoofguard_profile.create_or_overwrite(
  336. SPOOFGUARD_PROFILE_ID,
  337. profile_id=SPOOFGUARD_PROFILE_ID,
  338. address_binding_whitelist=True,
  339. tags=self.nsxpolicy.build_v3_api_version_tag())
  340. except nsx_lib_exc.StaleRevision as e:
  341. # This means that another controller is also creating this
  342. LOG.info("Failed to configure spoofguard_profile: %s", e)
  343. # No Port security spoofguard profile
  344. # (default NSX profile. just verify it exists)
  345. try:
  346. self.nsxpolicy.spoofguard_profile.get(NO_SPOOFGUARD_PROFILE_ID)
  347. except nsx_lib_exc.ResourceNotFound:
  348. msg = (_("Cannot find spoofguard profile %s") %
  349. NO_SPOOFGUARD_PROFILE_ID)
  350. raise nsx_exc.NsxPluginException(err_msg=msg)
  351. # Mac discovery profile (find it or create)
  352. try:
  353. self.nsxpolicy.mac_discovery_profile.get(
  354. MAC_DISCOVERY_PROFILE_ID)
  355. except nsx_lib_exc.ResourceNotFound:
  356. try:
  357. self.nsxpolicy.mac_discovery_profile.create_or_overwrite(
  358. MAC_DISCOVERY_PROFILE_ID,
  359. profile_id=MAC_DISCOVERY_PROFILE_ID,
  360. mac_change_enabled=True,
  361. mac_learning_enabled=True,
  362. tags=self.nsxpolicy.build_v3_api_version_tag())
  363. except nsx_lib_exc.StaleRevision as e:
  364. # This means that another controller is also creating this
  365. LOG.info("Failed to configure mac_discovery_profile: %s", e)
  366. # No Mac discovery profile profile
  367. # (default NSX profile. just verify it exists)
  368. try:
  369. self.nsxpolicy.mac_discovery_profile.get(
  370. NO_MAC_DISCOVERY_PROFILE_ID)
  371. except nsx_lib_exc.ResourceNotFound:
  372. msg = (_("Cannot find MAC discovery profile %s") %
  373. NO_MAC_DISCOVERY_PROFILE_ID)
  374. raise nsx_exc.NsxPluginException(err_msg=msg)
  375. # No Port security segment-security profile (find it or create)
  376. try:
  377. self.nsxpolicy.segment_security_profile.get(
  378. NO_SEG_SECURITY_PROFILE_ID)
  379. except nsx_lib_exc.ResourceNotFound:
  380. try:
  381. self.nsxpolicy.segment_security_profile.create_or_overwrite(
  382. NO_SEG_SECURITY_PROFILE_ID,
  383. profile_id=NO_SEG_SECURITY_PROFILE_ID,
  384. bpdu_filter_enable=False,
  385. dhcp_client_block_enabled=False,
  386. dhcp_client_block_v6_enabled=False,
  387. dhcp_server_block_enabled=False,
  388. dhcp_server_block_v6_enabled=False,
  389. non_ip_traffic_block_enabled=False,
  390. ra_guard_enabled=False,
  391. rate_limits_enabled=False,
  392. tags=self.nsxpolicy.build_v3_api_version_tag())
  393. except nsx_lib_exc.StaleRevision as e:
  394. # This means that another controller is also creating this
  395. LOG.info("Failed to configure segment_security_profile: %s", e)
  396. # Port security segment-security profile
  397. # (default NSX profile. just verify it exists)
  398. try:
  399. self.nsxpolicy.segment_security_profile.get(
  400. SEG_SECURITY_PROFILE_ID)
  401. except nsx_lib_exc.ResourceNotFound:
  402. msg = (_("Cannot find segment security profile %s") %
  403. SEG_SECURITY_PROFILE_ID)
  404. raise nsx_exc.NsxPluginException(err_msg=msg)
  405. # Ipv6 SLAAC NDRA profile (find it or create)
  406. try:
  407. self.nsxpolicy.ipv6_ndra_profile.get(SLAAC_NDRA_PROFILE_ID)
  408. except nsx_lib_exc.ResourceNotFound:
  409. try:
  410. self.nsxpolicy.ipv6_ndra_profile.create_or_overwrite(
  411. SLAAC_NDRA_PROFILE_ID,
  412. profile_id=SLAAC_NDRA_PROFILE_ID,
  413. ra_mode=policy_constants.IPV6_RA_MODE_SLAAC_RA,
  414. tags=self.nsxpolicy.build_v3_api_version_tag())
  415. except nsx_lib_exc.StaleRevision as e:
  416. # This means that another controller is also creating this
  417. LOG.info("Failed to configure ipv6_ndra_profile for SLAAC: %s",
  418. e)
  419. # Verify NO SLAAC NDRA profile (find it or create)
  420. try:
  421. self.nsxpolicy.ipv6_ndra_profile.get(NO_SLAAC_NDRA_PROFILE_ID)
  422. except nsx_lib_exc.ResourceNotFound:
  423. try:
  424. self.nsxpolicy.ipv6_ndra_profile.create_or_overwrite(
  425. NO_SLAAC_NDRA_PROFILE_ID,
  426. profile_id=NO_SLAAC_NDRA_PROFILE_ID,
  427. ra_mode=policy_constants.IPV6_RA_MODE_DISABLED,
  428. tags=self.nsxpolicy.build_v3_api_version_tag())
  429. except nsx_lib_exc.StaleRevision as e:
  430. # This means that another controller is also creating this
  431. LOG.info("Failed to configure ipv6_ndra_profile for NO SLAAC: "
  432. "%s", e)
  433. self.client_ssl_profile = None
  434. LOG.debug("Initializing NSX-P Load Balancer default profiles")
  435. try:
  436. self._init_lb_profiles()
  437. except Exception as e:
  438. msg = (_("Unable to initialize NSX-P lb profiles: "
  439. "Reason: %(reason)s") % {'reason': str(e)})
  440. raise nsx_exc.NsxPluginException(err_msg=msg)
  441. @staticmethod
  442. def plugin_type():
  443. return projectpluginmap.NsxPlugins.NSX_P
  444. @staticmethod
  445. def is_tvd_plugin():
  446. return False
  447. def _init_fwaas(self, with_rpc):
  448. if self.fwaas_callbacks:
  449. # already initialized
  450. return
  451. if fwaas_utils.is_fwaas_v2_plugin_enabled():
  452. LOG.info("NSXp FWaaS v2 plugin enabled")
  453. self.fwaas_callbacks = fwaas_callbacks_v2.NsxpFwaasCallbacksV2(
  454. with_rpc)
  455. def _get_octavia_stats_getter(self):
  456. return listener_mgr.stats_getter
  457. def _init_lb_profiles(self):
  458. ssl_profile_client = self.nsxpolicy.load_balancer.client_ssl_profile
  459. with locking.LockManager.get_lock('nsxp_lb_profiles_init'):
  460. try:
  461. ssl_profile_client.get(NSX_P_CLIENT_SSL_PROFILE)
  462. except nsx_lib_exc.ResourceNotFound:
  463. try:
  464. ssl_profile_client.create_or_overwrite(
  465. NSX_P_CLIENT_SSL_PROFILE,
  466. client_ssl_profile_id=NSX_P_CLIENT_SSL_PROFILE,
  467. description='Neutron LB Client SSL Profile',
  468. tags=self.nsxlib.build_v3_api_version_tag())
  469. except nsx_lib_exc.StaleRevision as e:
  470. # This means that another controller is also creating this
  471. LOG.info("Failed to configure LB client_ssl_profile: %s",
  472. e)
  473. self.client_ssl_profile = NSX_P_CLIENT_SSL_PROFILE
  474. def spawn_complete(self, resource, event, trigger, payload=None):
  475. # Init the FWaaS support with RPC listeners for the original process
  476. self._init_fwaas(with_rpc=True)
  477. self._init_octavia()
  478. self.octavia_stats_collector = (
  479. octavia_listener.NSXOctaviaStatisticsCollector(
  480. self,
  481. self._get_octavia_stats_getter()))
  482. def _init_octavia(self):
  483. octavia_objects = self._get_octavia_objects()
  484. self.octavia_listener = octavia_listener.NSXOctaviaListener(
  485. **octavia_objects)
  486. def _get_octavia_objects(self):
  487. return {
  488. 'loadbalancer': loadbalancer_mgr.EdgeLoadBalancerManagerFromDict(),
  489. 'listener': listener_mgr.EdgeListenerManagerFromDict(),
  490. 'pool': pool_mgr.EdgePoolManagerFromDict(),
  491. 'member': member_mgr.EdgeMemberManagerFromDict(),
  492. 'healthmonitor':
  493. healthmonitor_mgr.EdgeHealthMonitorManagerFromDict(),
  494. 'l7policy': l7policy_mgr.EdgeL7PolicyManagerFromDict(),
  495. 'l7rule': l7rule_mgr.EdgeL7RuleManagerFromDict()}
  496. def init_complete(self, resource, event, trigger, payload=None):
  497. with locking.LockManager.get_lock('plugin-init-complete'):
  498. if self.init_is_complete:
  499. # Should be called only once per worker
  500. return
  501. # reinitialize the cluster upon fork for api workers to ensure
  502. # each process has its own keepalive loops + state
  503. self.nsxpolicy.reinitialize_cluster(resource, event, trigger,
  504. payload=payload)
  505. if self.nsxlib:
  506. self.nsxlib.reinitialize_cluster(resource, event, trigger,
  507. payload=payload)
  508. # Init the FWaaS support without RPC listeners
  509. # for the spawn workers
  510. self._init_fwaas(with_rpc=False)
  511. # Init octavia listener and endpoints
  512. self._init_octavia()
  513. self.init_is_complete = True
  514. def _setup_rpc(self):
  515. self.endpoints = [agents_db.AgentExtRpcCallback()]
  516. def _create_network_on_backend(self, context, net_data,
  517. transparent_vlan,
  518. provider_data, az):
  519. net_data['id'] = net_data.get('id') or uuidutils.generate_uuid()
  520. # update the network name to indicate the neutron id too.
  521. net_name = utils.get_name_and_uuid(net_data['name'] or 'network',
  522. net_data['id'])
  523. tags = self.nsxpolicy.build_v3_tags_payload(
  524. net_data, resource_type='os-neutron-net-id',
  525. project_name=context.tenant_name)
  526. admin_state = net_data.get('admin_state_up', True)
  527. LOG.debug('create_network: %(net_name)s, %(physical_net)s, '
  528. '%(tags)s, %(admin_state)s, %(vlan_id)s',
  529. {'net_name': net_name,
  530. 'physical_net': provider_data['physical_net'],
  531. 'tags': tags,
  532. 'admin_state': admin_state,
  533. 'vlan_id': provider_data['vlan_id']})
  534. if transparent_vlan:
  535. # all vlan tags are allowed for guest vlan
  536. vlan_ids = ["0-%s" % const.MAX_VLAN_TAG]
  537. elif provider_data['vlan_id']:
  538. vlan_ids = [provider_data['vlan_id']]
  539. else:
  540. vlan_ids = None
  541. kwargs = {
  542. 'segment_id': net_data['id'],
  543. 'description': net_data.get('description'),
  544. 'vlan_ids': vlan_ids,
  545. 'transport_zone_id': provider_data['physical_net'],
  546. 'tags': tags}
  547. if az.use_policy_md:
  548. kwargs['metadata_proxy_id'] = az._native_md_proxy_uuid
  549. self.nsxpolicy.segment.create_or_overwrite(
  550. net_name, **kwargs)
  551. if not admin_state and cfg.CONF.nsx_p.allow_passthrough:
  552. # This api uses the passthrough api
  553. self.nsxpolicy.segment.set_admin_state(
  554. net_data['id'], admin_state)
  555. def _tier0_validator(self, tier0_uuid):
  556. # Fail if the tier0 uuid was not found on the NSX
  557. self.nsxpolicy.tier0.get(tier0_uuid)
  558. def _get_nsx_net_tz_id(self, nsx_net):
  559. return nsx_net['transport_zone_path'].split('/')[-1]
  560. def _allow_ens_networks(self):
  561. return True
  562. def _ens_psec_supported(self):
  563. """ENS security features are always enabled on NSX versions which
  564. the policy plugin supports.
  565. """
  566. return True
  567. def _ens_qos_supported(self):
  568. return self.nsxpolicy.feature_supported(
  569. nsxlib_consts.FEATURE_ENS_WITH_QOS)
  570. def _validate_ens_net_portsecurity(self, net_data):
  571. """ENS security features are always enabled on NSX versions which
  572. the policy plugin supports.
  573. So no validation is needed
  574. """
  575. pass
  576. def _assert_on_resource_admin_state_down(self, resource_data):
  577. """Network & port admin state is only supported with passthrough api"""
  578. if (not cfg.CONF.nsx_p.allow_passthrough and
  579. resource_data.get("admin_state_up") is False):
  580. err_msg = (_("admin_state_up=False is not supported when "
  581. "passthrough is disabled"))
  582. LOG.warning(err_msg)
  583. raise n_exc.InvalidInput(error_message=err_msg)
  584. def create_network(self, context, network):
  585. net_data = network['network']
  586. external = net_data.get(external_net.EXTERNAL)
  587. is_external_net = validators.is_attr_set(external) and external
  588. tenant_id = net_data['tenant_id']
  589. # validate the availability zone, and get the AZ object
  590. az = self._validate_obj_az_on_creation(context, net_data, 'network')
  591. self._ensure_default_security_group(context, tenant_id)
  592. vlt = False
  593. if extensions.is_extension_supported(self, 'vlan-transparent'):
  594. vlt = vlan_apidef.get_vlan_transparent(net_data)
  595. self._validate_create_network(context, net_data)
  596. self._assert_on_resource_admin_state_down(net_data)
  597. if is_external_net:
  598. is_provider_net, net_type, physical_net, vlan_id = (
  599. self._validate_external_net_create(
  600. net_data, az._default_tier0_router,
  601. self._tier0_validator))
  602. provider_data = {'is_provider_net': is_provider_net,
  603. 'net_type': net_type,
  604. 'physical_net': physical_net,
  605. 'vlan_id': vlan_id}
  606. is_backend_network = False
  607. else:
  608. provider_data = self._validate_provider_create(
  609. context, net_data, az,
  610. self.nsxpolicy.transport_zone,
  611. self.nsxpolicy.segment,
  612. transparent_vlan=vlt)
  613. if (provider_data['is_provider_net'] and
  614. provider_data['net_type'] ==
  615. utils.NsxV3NetworkTypes.NSX_NETWORK):
  616. is_backend_network = False
  617. else:
  618. is_backend_network = True
  619. # Create the neutron network
  620. with db_api.CONTEXT_WRITER.using(context):
  621. # Create network in Neutron
  622. created_net = super(NsxPolicyPlugin, self).create_network(
  623. context, network)
  624. net_id = created_net['id']
  625. if extensions.is_extension_supported(self, 'vlan-transparent'):
  626. super(NsxPolicyPlugin, self).update_network(
  627. context, net_id,
  628. {'network': {'vlan_transparent': vlt}})
  629. self._extension_manager.process_create_network(
  630. context, net_data, created_net)
  631. if psec.PORTSECURITY not in net_data:
  632. net_data[psec.PORTSECURITY] = True
  633. self._process_network_port_security_create(
  634. context, net_data, created_net)
  635. self._process_l3_create(context, created_net, net_data)
  636. self._add_az_to_net(context, net_id, net_data)
  637. if provider_data['is_provider_net']:
  638. # Save provider network fields, needed by get_network()
  639. net_bindings = [nsx_db.add_network_binding(
  640. context.session, net_id,
  641. provider_data['net_type'],
  642. provider_data['physical_net'],
  643. provider_data['vlan_id'])]
  644. self._extend_network_dict_provider(context, created_net,
  645. bindings=net_bindings)
  646. # Create the backend NSX network
  647. if is_backend_network:
  648. try:
  649. self._create_network_on_backend(
  650. context, created_net, vlt, provider_data, az)
  651. except Exception as e:
  652. LOG.exception("Failed to create NSX network network: %s", e)
  653. with excutils.save_and_reraise_exception():
  654. super(NsxPolicyPlugin, self).delete_network(
  655. context, net_id)
  656. # this extra lookup is necessary to get the
  657. # latest db model for the extension functions
  658. net_model = self._get_network(context, net_id)
  659. resource_extend.apply_funcs('networks', created_net, net_model)
  660. # MD Proxy is currently supported by the passthrough api only
  661. if (is_backend_network and not az.use_policy_md and
  662. cfg.CONF.nsx_p.allow_passthrough):
  663. try:
  664. # The new segment was not realized yet. Waiting for a bit.
  665. time.sleep(cfg.CONF.nsx_p.realization_wait_sec)
  666. nsx_net_id = self._get_network_nsx_id(context, net_id)
  667. self._create_net_mp_mdproxy_port(
  668. context, created_net, az, nsx_net_id)
  669. except Exception as e:
  670. LOG.exception("Failed to create mdproxy port for network %s: "
  671. "%s", net_id, e)
  672. with excutils.save_and_reraise_exception():
  673. self.delete_network(context, net_id)
  674. # Update the QoS policy (will affect only future compute ports)
  675. qos_com_utils.set_qos_policy_on_new_net(
  676. context, net_data, created_net)
  677. if net_data.get(qos_consts.QOS_POLICY_ID):
  678. LOG.info("QoS Policy %(qos)s will be applied to future compute "
  679. "ports of network %(net)s",
  680. {'qos': net_data[qos_consts.QOS_POLICY_ID],
  681. 'net': created_net['id']})
  682. return created_net
  683. def delete_network(self, context, network_id):
  684. if cfg.CONF.nsx_p.allow_passthrough:
  685. self._delete_network_disable_dhcp(context, network_id)
  686. is_nsx_net = self._network_is_nsx_net(context, network_id)
  687. is_external_net = self._network_is_external(context, network_id)
  688. # First call DB operation for delete network as it will perform
  689. # checks on active ports
  690. self._retry_delete_network(context, network_id)
  691. # Delete MD proxy port. This is relevant only if the plugin used
  692. # MP MD proxy when this network is created.
  693. # If not - the port will not be found, and it is ok.
  694. # Note(asarfaty): In the future this code can be removed.
  695. if (not is_external_net and cfg.CONF.nsx_p.allow_passthrough and
  696. not self.nsxpolicy.feature_supported(
  697. nsxlib_consts.FEATURE_NSX_POLICY_MDPROXY)):
  698. self._delete_nsx_port_by_network(network_id)
  699. # Delete the network segment from the backend
  700. if not is_external_net and not is_nsx_net:
  701. try:
  702. self.nsxpolicy.segment.delete(network_id)
  703. except nsx_lib_exc.ResourceNotFound:
  704. # If the resource was not found on the backend do not worry
  705. # about it. The conditions has already been logged, so there
  706. # is no need to do further logging
  707. pass
  708. except nsx_lib_exc.ManagerError as e:
  709. # If there is a failure in deleting the resource, fail the
  710. # neutron operation even though the neutron object was already
  711. # deleted. This way the user will be aware of zombie resources
  712. # that may fail future actions.
  713. msg = (_("Backend segment deletion for neutron network %(id)s "
  714. "failed. The object was however removed from the "
  715. "Neutron database: %(e)s") %
  716. {'id': network_id, 'e': e})
  717. raise nsx_exc.NsxPluginException(err_msg=msg)
  718. # Remove from caches
  719. if network_id in NET_NEUTRON_2_NSX_ID_CACHE:
  720. nsx_id = NET_NEUTRON_2_NSX_ID_CACHE[network_id]
  721. del NET_NEUTRON_2_NSX_ID_CACHE[network_id]
  722. del NET_NSX_2_NEUTRON_ID_CACHE[nsx_id]
  723. def update_network(self, context, network_id, network):
  724. original_net = super(NsxPolicyPlugin, self).get_network(
  725. context, network_id)
  726. net_data = network['network']
  727. # Validate the updated parameters
  728. self._validate_update_network(context, network_id, original_net,
  729. net_data)
  730. self._assert_on_resource_admin_state_down(net_data)
  731. # Neutron does not support changing provider network values
  732. utils.raise_if_updates_provider_attributes(net_data)
  733. extern_net = self._network_is_external(context, network_id)
  734. is_nsx_net = self._network_is_nsx_net(context, network_id)
  735. # Update the neutron network
  736. updated_net = super(NsxPolicyPlugin, self).update_network(
  737. context, network_id, network)
  738. self._extension_manager.process_update_network(context, net_data,
  739. updated_net)
  740. self._process_l3_update(context, updated_net, network['network'])
  741. self._extend_network_dict_provider(context, updated_net)
  742. if qos_consts.QOS_POLICY_ID in net_data:
  743. # attach the policy to the network in neutron DB
  744. #(will affect only future compute ports)
  745. qos_com_utils.update_network_policy_binding(
  746. context, network_id, net_data[qos_consts.QOS_POLICY_ID])
  747. updated_net[qos_consts.QOS_POLICY_ID] = net_data[
  748. qos_consts.QOS_POLICY_ID]
  749. if net_data[qos_consts.QOS_POLICY_ID]:
  750. LOG.info("QoS Policy %(qos)s will be applied to future "
  751. "compute ports of network %(net)s",
  752. {'qos': net_data[qos_consts.QOS_POLICY_ID],
  753. 'net': network_id})
  754. # Update the backend segment
  755. if (not extern_net and not is_nsx_net and
  756. ('name' in net_data or 'description' in net_data)):
  757. net_name = utils.get_name_and_uuid(
  758. updated_net['name'] or 'network', network_id)
  759. try:
  760. self.nsxpolicy.segment.update(
  761. network_id,
  762. name=net_name,
  763. description=updated_net.get('description', ''))
  764. except nsx_lib_exc.ManagerError:
  765. LOG.exception("Unable to update NSX backend, rolling "
  766. "back changes on neutron")
  767. with excutils.save_and_reraise_exception():
  768. # remove the AZ from the network before rollback because
  769. # it is read only, and breaks the rollback
  770. if 'availability_zone_hints' in original_net:
  771. del original_net['availability_zone_hints']
  772. super(NsxPolicyPlugin, self).update_network(
  773. context, network_id, {'network': original_net})
  774. if (not extern_net and not is_nsx_net and
  775. 'admin_state_up' in net_data and
  776. cfg.CONF.nsx_p.allow_passthrough):
  777. # Update admin state using the passthrough api
  778. self.nsxpolicy.segment.set_admin_state(
  779. network_id, net_data['admin_state_up'])
  780. return updated_net
  781. def _update_slaac_on_router(self, context, router_id,
  782. subnet, router_subnets, delete=False):
  783. # TODO(annak): redesign when policy supports downlink-level
  784. # ndra profile attachment
  785. # This code is optimised to deal with concurrency challenges
  786. # (which can not be always solved by lock because the plugin
  787. # can run on different hosts).
  788. # We prefer to make another backend call for attaching the
  789. # profile even if it is already attached, than rely on DB
  790. # to have an accurate picture of existing subnets.
  791. profile_id = None
  792. slaac_subnet = (subnet.get('ipv6_address_mode') == 'slaac')
  793. if slaac_subnet and not delete:
  794. # slaac subnet connected - verify slaac is set on router
  795. profile_id = SLAAC_NDRA_PROFILE_ID
  796. if delete:
  797. router_subnets = self._load_router_subnet_cidrs_from_db(
  798. context.elevated(), router_id)
  799. # check if there is another slaac overlay subnet that needs
  800. # advertising (vlan advertising is attached on interface level)
  801. slaac_subnets = [s for s in router_subnets
  802. if s['id'] != subnet['id'] and
  803. s.get('ipv6_address_mode') == 'slaac' and
  804. self._is_overlay_network(context,
  805. s['network_id'])]
  806. if not slaac_subnets and slaac_subnet:
  807. # this was the last slaac subnet connected -
  808. # need to disable slaac on router
  809. profile_id = NO_SLAAC_NDRA_PROFILE_ID
  810. if profile_id:
  811. self.nsxpolicy.tier1.update(router_id,
  812. ipv6_ndra_profile_id=profile_id)
  813. def create_subnet(self, context, subnet):
  814. return self._create_subnet(context, subnet)
  815. def delete_subnet(self, context, subnet_id):
  816. # Call common V3 code to delete the subnet
  817. super(NsxPolicyPlugin, self).delete_subnet(context, subnet_id)
  818. def update_subnet(self, context, subnet_id, subnet):
  819. return self._update_subnet(context, subnet_id, subnet)
  820. def _build_port_address_bindings(self, context, port_data):
  821. psec_on, has_ip = self._determine_port_security_and_has_ip(context,
  822. port_data)
  823. if not psec_on:
  824. return None
  825. address_bindings = []
  826. for fixed_ip in port_data['fixed_ips']:
  827. ip_addr = fixed_ip['ip_address']
  828. mac_addr = port_data['mac_address']
  829. binding = self.nsxpolicy.segment_port.build_address_binding(
  830. ip_addr, mac_addr)
  831. address_bindings.append(binding)
  832. # add address binding for link local ipv6 address, otherwise
  833. # neighbor discovery will be blocked by spoofguard.
  834. # for now only one ipv6 address is allowed
  835. if netaddr.IPAddress(ip_addr).version == 6:
  836. lladdr = netaddr.EUI(mac_addr).ipv6_link_local()
  837. binding = self.nsxpolicy.segment_port.build_address_binding(
  838. lladdr, mac_addr)
  839. address_bindings.append(binding)
  840. for pair in port_data.get(addr_apidef.ADDRESS_PAIRS):
  841. binding = self.nsxpolicy.segment_port.build_address_binding(
  842. pair['ip_address'], pair['mac_address'])
  843. address_bindings.append(binding)
  844. return address_bindings
  845. def _get_network_nsx_id(self, context, network_id):
  846. """Return the id of this logical switch in the nsx manager
  847. This api waits for the segment to really be realized, and return the ID
  848. of the NSX logical switch.
  849. If it was not realized or timed out retrying, it will return None
  850. The nova api will use this to attach to the instance.
  851. """
  852. if network_id in NET_NEUTRON_2_NSX_ID_CACHE:
  853. return NET_NEUTRON_2_NSX_ID_CACHE[network_id]
  854. if not self._network_is_external(context, network_id):
  855. segment_id = self._get_network_nsx_segment_id(context, network_id)
  856. try:
  857. nsx_id = self.nsxpolicy.segment.get_realized_logical_switch_id(
  858. segment_id)
  859. # Add result to caches
  860. NET_NEUTRON_2_NSX_ID_CACHE[network_id] = nsx_id
  861. NET_NSX_2_NEUTRON_ID_CACHE[nsx_id] = network_id
  862. return nsx_id
  863. except nsx_lib_exc.ManagerError:
  864. LOG.error("Network %s was not realized", network_id)
  865. # Do not cache this result
  866. else:
  867. # Add empty result to cache
  868. NET_NEUTRON_2_NSX_ID_CACHE[network_id] = None
  869. def _get_network_nsx_segment_id(self, context, network_id):
  870. """Return the NSX segment ID matching the neutron network id
  871. Usually the NSX ID is the same as the neutron ID. The exception is
  872. when this is a provider NSX_NETWORK, which means the network already
  873. existed on the NSX backend, and it is being consumed by the plugin.
  874. """
  875. bindings = nsx_db.get_network_bindings(context.session, network_id)
  876. if (bindings and
  877. bindings[0].binding_type == utils.NsxV3NetworkTypes.NSX_NETWORK):
  878. # return the ID of the NSX network
  879. return bindings[0].phy_uuid
  880. return network_id
  881. def _build_port_tags(self, port_data):
  882. sec_groups = []
  883. sec_groups.extend(port_data.get(ext_sg.SECURITYGROUPS, []))
  884. sec_groups.extend(port_data.get(provider_sg.PROVIDER_SECURITYGROUPS,
  885. []))
  886. tags = []
  887. for sg in sec_groups:
  888. tags = nsxlib_utils.add_v3_tag(tags,
  889. NSX_P_SECURITY_GROUP_TAG,
  890. sg)
  891. return tags
  892. def _create_or_update_port_on_backend(self, context, port_data, is_psec_on,
  893. qos_policy_id, original_port=None):
  894. is_create = original_port is None
  895. is_update = not is_create
  896. name = self._build_port_name(context, port_data)
  897. address_bindings = self._build_port_address_bindings(
  898. context, port_data)
  899. device_owner = port_data.get('device_owner')
  900. vif_id = None
  901. if device_owner and device_owner != l3_db.DEVICE_OWNER_ROUTER_INTF:
  902. vif_id = port_data['id']
  903. tags = self._build_port_tags(port_data)
  904. if device_owner == const.DEVICE_OWNER_DHCP:
  905. tag_resource_type = 'os-neutron-dport-id'
  906. elif device_owner == l3_db.DEVICE_OWNER_ROUTER_INTF:
  907. tag_resource_type = 'os-neutron-rport-id'
  908. else:
  909. tag_resource_type = NSX_P_PORT_RESOURCE_TYPE
  910. tags.extend(self.nsxpolicy.build_v3_tags_payload(
  911. port_data, resource_type=tag_resource_type,
  912. project_name=context.tenant_name))
  913. if self._is_excluded_port(device_owner, is_psec_on):
  914. tags.append({'scope': security.PORT_SG_SCOPE,
  915. 'tag': NSX_P_EXCLUDE_LIST_TAG})
  916. if self.support_external_port_tagging:
  917. external_tags = self.get_external_tags_for_port(
  918. context, port_data['id'])
  919. if external_tags:
  920. total_len = len(external_tags) + len(tags)
  921. if total_len > nsxlib_utils.MAX_TAGS:
  922. LOG.warning("Cannot add external tags to port %s: "
  923. "too many tags", port_data['id'])
  924. else:
  925. tags.extend(external_tags)
  926. segment_id = self._get_network_nsx_segment_id(
  927. context, port_data['network_id'])
  928. # Calculate the port security profiles
  929. if is_psec_on:
  930. spoofguard_profile = SPOOFGUARD_PROFILE_ID
  931. seg_sec_profile = SEG_SECURITY_PROFILE_ID
  932. else:
  933. spoofguard_profile = NO_SPOOFGUARD_PROFILE_ID
  934. seg_sec_profile = NO_SEG_SECURITY_PROFILE_ID
  935. mac_disc_profile_must = False
  936. if is_psec_on:
  937. address_pairs = port_data.get(addr_apidef.ADDRESS_PAIRS)
  938. if validators.is_attr_set(address_pairs) and address_pairs:
  939. mac_disc_profile_must = True
  940. mac_learning_enabled = (
  941. validators.is_attr_set(port_data.get(mac_ext.MAC_LEARNING)) and
  942. port_data.get(mac_ext.MAC_LEARNING) is True)
  943. if mac_disc_profile_must or mac_learning_enabled:
  944. mac_discovery_profile = MAC_DISCOVERY_PROFILE_ID
  945. else:
  946. mac_discovery_profile = NO_MAC_DISCOVERY_PROFILE_ID
  947. # Prepare the args for the segment port creation
  948. kwargs = {'port_id': port_data['id'],
  949. 'description': port_data.get('description', ''),
  950. 'address_bindings': address_bindings,
  951. 'tags': tags}
  952. if vif_id:
  953. kwargs['vif_id'] = vif_id
  954. # Create/ update the backend port in a single transaction
  955. with policy_trans.NsxPolicyTransaction():
  956. self.nsxpolicy.segment_port.create_or_overwrite(
  957. name, segment_id, **kwargs)
  958. # add the security profiles to the port
  959. self.nsxpolicy.segment_port_security_profiles.create_or_overwrite(
  960. name, segment_id, port_data['id'],
  961. spoofguard_profile_id=spoofguard_profile,
  962. segment_security_profile_id=seg_sec_profile)
  963. # add the mac discovery profile to the port
  964. self.nsxpolicy.segment_port_discovery_profiles.create_or_overwrite(
  965. name, segment_id, port_data['id'],
  966. mac_discovery_profile_id=mac_discovery_profile)
  967. # Add QoS segment profile (only if QoS is enabled)
  968. if directory.get_plugin(plugin_const.QOS):
  969. self.nsxpolicy.segment_port_qos_profiles.create_or_overwrite(
  970. name, segment_id, port_data['id'],
  971. qos_profile_id=qos_policy_id)
  972. # Update port admin status using passthrough api, only if it changed
  973. # or new port with disabled admin state
  974. if cfg.CONF.nsx_p.allow_passthrough and 'admin_state_up' in port_data:
  975. new_state = port_data['admin_state_up']
  976. if ((is_create and new_state is False) or
  977. (is_update and
  978. original_port.get('admin_state_up') != new_state)):
  979. # This api uses the passthrough api
  980. self.nsxpolicy.segment_port.set_admin_state(
  981. segment_id, port_data['id'], new_state)
  982. def base_create_port(self, context, port):
  983. neutron_db = super(NsxPolicyPlugin, self).create_port(context, port)
  984. self._extension_manager.process_create_port(
  985. context, port['port'], neutron_db)
  986. return neutron_db
  987. def _is_backend_port(self, context, port_data):
  988. is_external_net = self._network_is_external(
  989. context, port_data['network_id'])
  990. device_owner = port_data.get('device_owner')
  991. is_router_interface = (device_owner == l3_db.DEVICE_OWNER_ROUTER_INTF)
  992. is_dhcp_port = (device_owner == const.DEVICE_OWNER_DHCP)
  993. if is_external_net or is_router_interface or is_dhcp_port:
  994. # DHCP is handled on MP level so far
  995. # Router is connected automatically in policy
  996. return False
  997. return True
  998. def create_port(self, context, port, l2gw_port_check=False):
  999. port_data = port['port']
  1000. # validate the new port parameters
  1001. self._validate_create_port(context, port_data)
  1002. self._assert_on_resource_admin_state_down(port_data)
  1003. # Validate the vnic type (the same types as for the NSX-T plugin)
  1004. direct_vnic_type = self._validate_port_vnic_type(
  1005. context, port_data, port_data['network_id'],
  1006. projectpluginmap.NsxPlugins.NSX_T)
  1007. is_external_net = self._network_is_external(
  1008. context, port_data['network_id'])
  1009. if is_external_net:
  1010. self._assert_on_external_net_with_compute(port_data)
  1011. with db_api.CONTEXT_WRITER.using(context):
  1012. neutron_db = self.base_create_port(context, port)
  1013. port["port"].update(neutron_db)
  1014. self.fix_direct_vnic_port_sec(direct_vnic_type, port_data)
  1015. (is_psec_on, has_ip, sgids, psgids) = (
  1016. self._create_port_preprocess_security(context, port,
  1017. port_data, neutron_db,
  1018. False))
  1019. self._process_portbindings_create_and_update(
  1020. context, port['port'], port_data,
  1021. vif_type=self._vif_type_by_vnic_type(direct_vnic_type))
  1022. self._process_port_create_extra_dhcp_opts(
  1023. context, port_data,
  1024. port_data.get(ext_edo.EXTRADHCPOPTS))
  1025. self._process_port_create_security_group(context, port_data, sgids)
  1026. self._process_port_create_provider_security_group(
  1027. context, port_data, psgids)
  1028. # Handle port mac learning
  1029. if validators.is_attr_set(port_data.get(mac_ext.MAC_LEARNING)):
  1030. # Make sure mac_learning and port sec are not both enabled
  1031. if port_data.get(mac_ext.MAC_LEARNING) and is_psec_on:
  1032. msg = _('Mac learning requires that port security be '
  1033. 'disabled')
  1034. LOG.error(msg)
  1035. raise n_exc.InvalidInput(error_message=msg)
  1036. # save the mac learning value in the DB
  1037. self._create_mac_learning_state(context, port_data)
  1038. elif mac_ext.MAC_LEARNING in port_data:
  1039. # This is due to the fact that the default is
  1040. # ATTR_NOT_SPECIFIED
  1041. port_data.pop(mac_ext.MAC_LEARNING)
  1042. qos_policy_id = self._get_port_qos_policy_id(
  1043. context, None, port_data)
  1044. if self._is_backend_port(context, port_data):
  1045. # router interface port is created automatically by policy
  1046. try:
  1047. self._create_or_update_port_on_backend(
  1048. context, port_data, is_psec_on, qos_policy_id)
  1049. except Exception as e:
  1050. with excutils.save_and_reraise_exception():
  1051. LOG.error('Failed to create port %(id)s on NSX '
  1052. 'backend. Exception: %(e)s',
  1053. {'id': neutron_db['id'], 'e': e})
  1054. super(NsxPolicyPlugin, self).delete_port(
  1055. context, neutron_db['id'])
  1056. # Attach the policy to the port in the neutron DB
  1057. if qos_policy_id:
  1058. qos_com_utils.update_port_policy_binding(context,
  1059. neutron_db['id'],
  1060. qos_policy_id)
  1061. # this extra lookup is necessary to get the
  1062. # latest db model for the extension functions
  1063. port_model = self._get_port(context, port_data['id'])
  1064. resource_extend.apply_funcs('ports', port_data, port_model)
  1065. self._extend_nsx_port_dict_binding(context, port_data)
  1066. self._remove_provider_security_groups_from_list(port_data)
  1067. # Add Mac/IP binding to native DHCP server and neutron DB.
  1068. if cfg.CONF.nsx_p.allow_passthrough:
  1069. try:
  1070. self._add_dhcp_binding(context, port_data)
  1071. except nsx_lib_exc.ManagerError:
  1072. # Rollback create port
  1073. self.delete_port(context, port_data['id'],
  1074. force_delete_dhcp=True)
  1075. msg = _('Unable to create port. Please contact admin')
  1076. LOG.exception(msg)
  1077. raise nsx_exc.NsxPluginException(err_msg=msg)
  1078. kwargs = {'context': context, 'port': neutron_db}
  1079. registry.notify(resources.PORT, events.AFTER_CREATE, self, **kwargs)
  1080. return port_data
  1081. def _delete_port_on_backend(self, context, net_id, port_id):
  1082. try:
  1083. segment_id = self._get_network_nsx_segment_id(context, net_id)
  1084. self.nsxpolicy.segment_port_security_profiles.delete(
  1085. segment_id, port_id)
  1086. self.nsxpolicy.segment_port_discovery_profiles.delete(
  1087. segment_id, port_id)
  1088. if directory.get_plugin(plugin_const.QOS):
  1089. self.nsxpolicy.segment_port_qos_profiles.delete(
  1090. segment_id, port_id)
  1091. self.nsxpolicy.segment_port.delete(segment_id, port_id)
  1092. except nsx_lib_exc.ResourceNotFound:
  1093. # If the resource was not found on the backend do not worry about
  1094. # it. The conditions has already been logged, so there is no need
  1095. # to do further logging
  1096. pass
  1097. except nsx_lib_exc.ManagerError as e:
  1098. # If there is a failure in deleting the resource.
  1099. # In this case the neutron port was not deleted yet.
  1100. msg = (_("Backend port deletion for neutron port %(id)s "
  1101. "failed: %(e)s") % {'id': port_id, 'e': e})
  1102. raise nsx_exc.NsxPluginException(err_msg=msg)
  1103. def delete_port(self, context, port_id,
  1104. l3_port_check=True, l2gw_port_check=True,
  1105. force_delete_dhcp=False,
  1106. force_delete_vpn=False):
  1107. # first update neutron (this will perform all types of validations)
  1108. port_data = self.get_port(context, port_id)
  1109. net_id = port_data['network_id']
  1110. # if needed, check to see if this is a port owned by
  1111. # a l3 router. If so, we should prevent deletion here
  1112. if l3_port_check:
  1113. self.prevent_l3_port_deletion(context, port_id)
  1114. port = self.get_port(context, port_id)
  1115. # Prevent DHCP port deletion if native support is enabled
  1116. if (cfg.CONF.nsx_p.allow_passthrough and
  1117. not force_delete_dhcp and
  1118. port['device_owner'] in [const.DEVICE_OWNER_DHCP]):
  1119. msg = (_('Can not delete DHCP port %s') % port_id)
  1120. raise n_exc.BadRequest(resource='port', msg=msg)
  1121. if not force_delete_vpn:
  1122. self._assert_on_vpn_port_change(port)
  1123. if self._is_backend_port(context, port_data):
  1124. self._delete_port_on_backend(context, net_id, port_id)
  1125. self.disassociate_floatingips(context, port_id)
  1126. # Remove Mac/IP binding from native DHCP server and neutron DB.
  1127. if cfg.CONF.nsx_p.allow_passthrough:
  1128. self._delete_dhcp_binding(context, port)
  1129. super(NsxPolicyPlugin, self).delete_port(context, port_id)
  1130. def _update_port_on_backend(self, context, lport_id,
  1131. original_port, updated_port,
  1132. is_psec_on, qos_policy_id):
  1133. # For now port create and update are the same
  1134. # Update might evolve with more features
  1135. return self._create_or_update_port_on_backend(
  1136. context, updated_port, is_psec_on,
  1137. qos_policy_id, original_port=original_port)
  1138. def update_port(self, context, port_id, port):
  1139. with db_api.CONTEXT_WRITER.using(context):
  1140. # get the original port, and keep it honest as it is later used
  1141. # for notifications
  1142. original_port = super(NsxPolicyPlugin, self).get_port(
  1143. context, port_id)
  1144. self._remove_provider_security_groups_from_list(original_port)
  1145. port_data = port['port']
  1146. self._validate_update_port(context, port_id, original_port,
  1147. port_data)
  1148. self._assert_on_resource_admin_state_down(port_data)
  1149. validate_port_sec = self._should_validate_port_sec_on_update_port(
  1150. port_data)
  1151. is_external_net = self._network_is_external(
  1152. context, original_port['network_id'])
  1153. if is_external_net:
  1154. self._assert_on_external_net_with_compute(port_data)
  1155. device_owner = (port_data['device_owner']
  1156. if 'device_owner' in port_data
  1157. else original_port.get('device_owner'))
  1158. self._validate_max_ips_per_port(context,
  1159. port_data.get('fixed_ips', []),
  1160. device_owner)
  1161. direct_vnic_type = self._validate_port_vnic_type(
  1162. context, port_data, original_port['network_id'])
  1163. updated_port = super(NsxPolicyPlugin, self).update_port(
  1164. context, port_id, port)
  1165. self._extension_manager.process_update_port(context, port_data,
  1166. updated_port)
  1167. # copy values over - except fixed_ips as
  1168. # they've already been processed
  1169. port_data.pop('fixed_ips', None)
  1170. updated_port.update(port_data)
  1171. updated_port = self._update_port_preprocess_security(
  1172. context, port, port_id, updated_port, False,
  1173. validate_port_sec=validate_port_sec,
  1174. direct_vnic_type=direct_vnic_type)
  1175. self._update_extra_dhcp_opts_on_port(context, port_id, port,
  1176. updated_port)
  1177. sec_grp_updated = self.update_security_group_on_port(
  1178. context, port_id, port, original_port, updated_port)
  1179. self._process_port_update_provider_security_group(
  1180. context, port, original_port, updated_port)
  1181. (port_security, has_ip) = self._determine_port_security_and_has_ip(
  1182. context, updated_port)
  1183. self._process_portbindings_create_and_update(
  1184. context, port_data, updated_port,
  1185. vif_type=self._vif_type_by_vnic_type(direct_vnic_type))
  1186. self._extend_nsx_port_dict_binding(context, updated_port)
  1187. mac_learning_state = updated_port.get(mac_ext.MAC_LEARNING)
  1188. if mac_learning_state is not None:
  1189. if port_security and mac_learning_state:
  1190. msg = _('Mac learning requires that port security be '
  1191. 'disabled')
  1192. LOG.error(msg)
  1193. raise n_exc.InvalidInput(error_message=msg)
  1194. self._update_mac_learning_state(context, port_id,
  1195. mac_learning_state)
  1196. self._remove_provider_security_groups_from_list(updated_port)
  1197. # Update the QoS policy
  1198. qos_policy_id = self._get_port_qos_policy_id(
  1199. context, original_port, updated_port)
  1200. qos_com_utils.update_port_policy_binding(context, port_id,
  1201. qos_policy_id)
  1202. # update the port in the backend, only if it exists in the DB
  1203. # (i.e not external net) and is not router interface
  1204. if self._is_backend_port(context, updated_port):
  1205. try:
  1206. self._update_port_on_backend(context, port_id,
  1207. original_port, updated_port,
  1208. port_security, qos_policy_id)
  1209. except Exception as e:
  1210. LOG.error('Failed to update port %(id)s on NSX '
  1211. 'backend. Exception: %(e)s',
  1212. {'id': port_id, 'e': e})
  1213. # Rollback the change
  1214. with excutils.save_and_reraise_exception():
  1215. with db_api.CONTEXT_WRITER.using(context):
  1216. self._revert_neutron_port_update(
  1217. context, port_id, original_port, updated_port,
  1218. port_security, sec_grp_updated)
  1219. else:
  1220. # if this port changed ownership to router interface, it should
  1221. # be deleted from policy, since policy handles router connectivity
  1222. original_owner = original_port.get('device_owner')
  1223. new_owner = port_data.get('device_owner')
  1224. if (original_owner != new_owner and
  1225. new_owner == const.DEVICE_OWNER_ROUTER_INTF):
  1226. self._delete_port_on_backend(context,
  1227. original_port['network_id'],
  1228. port_id)
  1229. # Update DHCP bindings.
  1230. if cfg.CONF.nsx_p.allow_passthrough:
  1231. self._update_dhcp_binding(context, original_port, updated_port)
  1232. # Make sure the port revision is updated
  1233. if 'revision_number' in updated_port:
  1234. port_model = self._get_port(context, port_id)
  1235. updated_port['revision_number'] = port_model.revision_number
  1236. # Notifications must be sent after the above transaction is complete
  1237. kwargs = {
  1238. 'context': context,
  1239. 'port': updated_port,
  1240. 'mac_address_updated': False,
  1241. 'original_port': original_port,
  1242. }
  1243. registry.notify(resources.PORT, events.AFTER_UPDATE, self, **kwargs)
  1244. return updated_port
  1245. def get_port(self, context, id, fields=None):
  1246. port = super(NsxPolicyPlugin, self).get_port(
  1247. context, id, fields=None)
  1248. self._extend_nsx_port_dict_binding(context, port)
  1249. self._extend_qos_port_dict_binding(context, port)
  1250. self._remove_provider_security_groups_from_list(port)
  1251. return db_utils.resource_fields(port, fields)
  1252. def get_ports(self, context, filters=None, fields=None,
  1253. sorts=None, limit=None, marker=None,
  1254. page_reverse=False):
  1255. filters = filters or {}
  1256. self._update_filters_with_sec_group(context, filters)
  1257. with db_api.CONTEXT_READER.using(context):
  1258. ports = (
  1259. super(NsxPolicyPlugin, self).get_ports(
  1260. context, filters, fields, sorts,
  1261. limit, marker, page_reverse))
  1262. # Add port extensions
  1263. for port in ports[:]:
  1264. self._extend_nsx_port_dict_binding(context, port)
  1265. self._extend_qos_port_dict_binding(context, port)
  1266. self._remove_provider_security_groups_from_list(port)
  1267. return (ports if not fields else
  1268. [db_utils.resource_fields(port, fields) for port in ports])
  1269. def _add_subnet_snat_rule(self, context, router_id, subnet,
  1270. gw_address_scope, gw_ip):
  1271. if not self._need_router_snat_rules(context, router_id, subnet,
  1272. gw_address_scope):
  1273. return
  1274. self.nsxpolicy.tier1_nat_rule.create_or_overwrite(
  1275. 'snat for subnet %s' % subnet['id'],
  1276. router_id,
  1277. nat_rule_id=self._get_snat_rule_id(subnet),
  1278. action=policy_constants.NAT_ACTION_SNAT,
  1279. sequence_number=NAT_RULE_PRIORITY_GW,
  1280. translated_network=gw_ip,
  1281. source_network=subnet['cidr'],
  1282. firewall_match=policy_constants.NAT_FIREWALL_MATCH_INTERNAL)
  1283. def _get_snat_rule_id(self, subnet):
  1284. return 'S-' + subnet['id']
  1285. def _get_no_dnat_rule_id(self, subnet):
  1286. return 'ND-' + subnet['id']
  1287. def _add_subnet_no_dnat_rule(self, context, router_id, subnet):
  1288. if not self._need_router_no_dnat_rules(subnet):
  1289. return
  1290. # Add NO-DNAT rule to allow internal traffic between VMs, even if
  1291. # they have floating ips (Only for routers with snat enabled)
  1292. self.nsxpolicy.tier1_nat_rule.create_or_overwrite(
  1293. 'no-dnat for subnet %s' % subnet['id'],
  1294. router_id,
  1295. nat_rule_id=self._get_no_dnat_rule_id(subnet),
  1296. action=policy_constants.NAT_ACTION_NO_DNAT,
  1297. sequence_number=NAT_RULE_PRIORITY_GW,
  1298. destination_network=subnet['cidr'],
  1299. firewall_match=policy_constants.NAT_FIREWALL_MATCH_BYPASS)
  1300. def _del_subnet_no_dnat_rule(self, router_id, subnet):
  1301. # Delete the previously created NO-DNAT rules
  1302. self.nsxpolicy.tier1_nat_rule.delete(
  1303. router_id,
  1304. nat_rule_id=self._get_no_dnat_rule_id(subnet))
  1305. def _del_subnet_snat_rule(self, router_id, subnet):
  1306. # Delete the previously created SNAT rules
  1307. self.nsxpolicy.tier1_nat_rule.delete(
  1308. router_id,
  1309. nat_rule_id=self._get_snat_rule_id(subnet))
  1310. def _get_edge_cluster_path(self, tier0_uuid, router):
  1311. # Take the AZ edge cluster if configured
  1312. az = self._get_router_az_obj(router)
  1313. if az and az._edge_cluster_uuid:
  1314. ec_id = az._edge_cluster_uuid
  1315. # get the full path of the edge cluster (no backend call)
  1316. return self.nsxpolicy.edge_cluster.get_path(ec_id)
  1317. # Get the current tier0 edge cluster (cached call)
  1318. return self.nsxpolicy.tier0.get_edge_cluster_path(
  1319. tier0_uuid)
  1320. def service_router_has_services(self, context, router_id, router=None):
  1321. """Check if the neutron router has any services
  1322. which require a backend service router
  1323. currently those are: SNAT, Loadbalancer, Edge firewall
  1324. """
  1325. if not router:
  1326. router = self._get_router(context, router_id)
  1327. snat_exist = router.enable_snat
  1328. fw_exist = self._router_has_edge_fw_rules(context, router)
  1329. vpn_exist = self.service_router_has_vpnaas(context, router_id)
  1330. lb_exist = False
  1331. if not (fw_exist or snat_exist or vpn_exist):
  1332. lb_exist = self.service_router_has_loadbalancers(
  1333. context, router_id)
  1334. return snat_exist or lb_exist or fw_exist or vpn_exist
  1335. def service_router_has_loadbalancers(self, context, router_id):
  1336. tags_to_search = [{'scope': lb_const.LR_ROUTER_TYPE, 'tag': router_id}]
  1337. router_lb_services = self.nsxpolicy.search_by_tags(
  1338. tags_to_search,
  1339. self.nsxpolicy.load_balancer.lb_service.entry_def.resource_type()
  1340. )['results']
  1341. non_delete_services = [srv for srv in router_lb_services
  1342. if not srv.get('marked_for_delete')]
  1343. return True if non_delete_services else False
  1344. def service_router_has_vpnaas(self, context, router_id):
  1345. """Return True if there is a vpn service attached to this router"""
  1346. vpn_plugin = directory.get_plugin(plugin_const.VPN)
  1347. if vpn_plugin:
  1348. filters = {'router_id': [router_id]}
  1349. if vpn_plugin.get_vpnservices(context.elevated(), filters=filters):
  1350. return True
  1351. return False
  1352. def verify_sr_at_backend(self, router_id):
  1353. """Check if the backend Tier1 has a service router or not"""
  1354. if self.nsxpolicy.tier1.get_edge_cluster_path(router_id):
  1355. return True
  1356. def _wait_until_edge_cluster_realized(self, router_id):
  1357. """Wait until MP logical router has an edge-cluster
  1358. Since currently the locale-services has no realization info,
  1359. And some actions should be performed only after it was realized,
  1360. this method checks the MP Lr for its edge-cluster id until it is set.
  1361. """
  1362. if not cfg.CONF.nsx_p.allow_passthrough:
  1363. return
  1364. lr_id = self.nsxpolicy.tier1.get_realized_id(
  1365. router_id, entity_type='RealizedLogicalRouter')
  1366. if not lr_id:
  1367. LOG.error("_wait_until_edge_cluster_realized Failed: No MP id "
  1368. "found for Tier1 %s", router_id)
  1369. return
  1370. test_num = 0
  1371. max_attempts = cfg.CONF.nsx_p.realization_max_attempts
  1372. sleep = cfg.CONF.nsx_p.realization_wait_sec
  1373. while test_num < max_attempts:
  1374. # get all the realized resources of the tier1
  1375. lr = self.nsxlib.logical_router.get(lr_id)
  1376. if lr.get('edge_cluster_id'):
  1377. break
  1378. time.sleep(sleep)
  1379. test_num += 1
  1380. if lr.get('edge_cluster_id'):
  1381. LOG.debug("MP LR %s of Tier1 %s edge cluster %s was set after %s "
  1382. "attempts", lr_id, router_id, lr.get('edge_cluster_id'),
  1383. test_num + 1)
  1384. else:
  1385. LOG.error("MP LR %s if Tier1 %s edge cluster was not set after %s "
  1386. "attempts", lr_id, router_id, test_num + 1)
  1387. def create_service_router(self, context, router_id, router=None,
  1388. update_firewall=True):
  1389. """Create a service router and enable standby relocation"""
  1390. if not router:
  1391. router = self._get_router(context, router_id)
  1392. tier0_uuid = self._get_tier0_uuid_by_router(context, router)
  1393. if not tier0_uuid:
  1394. err_msg = (_("Cannot create service router for %s without a "
  1395. "gateway") % router_id)
  1396. raise n_exc.InvalidInput(error_message=err_msg)
  1397. edge_cluster_path = self._get_edge_cluster_path(
  1398. tier0_uuid, router)
  1399. if edge_cluster_path:
  1400. self.nsxpolicy.tier1.set_edge_cluster_path(
  1401. router_id, edge_cluster_path)
  1402. else:
  1403. LOG.error("Tier0 %s does not have an edge cluster",
  1404. tier0_uuid)
  1405. try:
  1406. # Enable standby relocation & FW on this router
  1407. self.nsxpolicy.tier1.update(
  1408. router['id'], disable_firewall=False,
  1409. enable_standby_relocation=True)
  1410. except Exception as ex:
  1411. LOG.warning("Failed to enable standby relocation for router "
  1412. "%s: %s", router_id, ex)
  1413. # Validate locale-services realization before additional tier1 config
  1414. self._wait_until_edge_cluster_realized(router_id)
  1415. # update firewall rules (there might be FW group waiting for a
  1416. # service router)
  1417. if update_firewall:
  1418. self.update_router_firewall(context, router_id)
  1419. def delete_service_router(self, router_id):
  1420. """Delete the Tier1 service router by removing its edge cluster
  1421. Before that - disable all the features that require the service
  1422. router to exist.
  1423. """
  1424. # remove the gateway firewall policy
  1425. if self.fwaas_callbacks and self.fwaas_callbacks.fwaas_enabled:
  1426. self.fwaas_callbacks.delete_router_gateway_policy(router_id)
  1427. # Disable gateway firewall and standby relocation
  1428. self.nsxpolicy.tier1.update(
  1429. router_id, disable_firewall=True, enable_standby_relocation=False)
  1430. # remove the edge cluster from the tier1 router
  1431. self.nsxpolicy.tier1.remove_edge_cluster(router_id)
  1432. def _update_router_gw_info(self, context, router_id, info,
  1433. called_from=None):
  1434. # Get the original data of the router GW
  1435. router = self._get_router(context, router_id)
  1436. orig_info = self._get_router_gw_info(context, router_id)
  1437. org_tier0_uuid = self._get_tier0_uuid_by_router(context, router)
  1438. org_enable_snat = router.enable_snat
  1439. orgaddr, orgmask, _orgnexthop = (
  1440. self._get_external_attachment_info(
  1441. context, router))
  1442. router_subnets = self._load_router_subnet_cidrs_from_db(
  1443. context.elevated(), router_id)
  1444. self._validate_router_gw_and_tz(context, router_id, info,
  1445. org_enable_snat, router_subnets)
  1446. # Interface subnets cannot overlap with the GW external subnet
  1447. if info and info.get('network_id'):
  1448. self._validate_gw_overlap_interfaces(
  1449. context, info['network_id'],
  1450. [sub['network_id'] for sub in router_subnets])
  1451. # First update the neutron DB
  1452. super(NsxPolicyPlugin, self)._update_router_gw_info(
  1453. context, router_id, info, router=router)
  1454. # Get the new tier0 of the updated router (or None if GW was removed)
  1455. new_tier0_uuid = self._get_tier0_uuid_by_router(context, router)
  1456. new_enable_snat = router.enable_snat
  1457. newaddr, newmask, _newnexthop = self._get_external_attachment_info(
  1458. context, router)
  1459. sr_currently_exists = self.verify_sr_at_backend(router_id)
  1460. fw_exist = self._router_has_edge_fw_rules(context, router)
  1461. vpn_exist = self.service_router_has_vpnaas(context, router_id)
  1462. lb_exist = False
  1463. if not (fw_exist or vpn_exist):
  1464. # This is a backend call, so do it only if must
  1465. lb_exist = self.service_router_has_loadbalancers(
  1466. context, router_id)
  1467. tier1_services_exist = fw_exist or vpn_exist or lb_exist
  1468. actions = self._get_update_router_gw_actions(
  1469. org_tier0_uuid, orgaddr, org_enable_snat,
  1470. new_tier0_uuid, newaddr, new_enable_snat,
  1471. tier1_services_exist, sr_currently_exists)
  1472. try:
  1473. if actions['add_service_router']:
  1474. self.create_service_router(context, router_id, router=router)
  1475. if actions['remove_snat_rules']:
  1476. for subnet in router_subnets:
  1477. self._del_subnet_snat_rule(router_id, subnet)
  1478. if actions['remove_no_dnat_rules']:
  1479. for subnet in router_subnets:
  1480. self._del_subnet_no_dnat_rule(router_id, subnet)
  1481. if (actions['remove_router_link_port'] or
  1482. actions['add_router_link_port']):
  1483. # GW was changed. update GW and route advertisement
  1484. # pylint: disable=unexpected-keyword-arg
  1485. self.nsxpolicy.tier1.update_route_advertisement(
  1486. router_id,
  1487. static_routes=not new_enable_snat,
  1488. nat=actions['advertise_route_nat_flag'],
  1489. subnets=actions['advertise_route_connected_flag'],
  1490. tier0=new_tier0_uuid)
  1491. else:
  1492. # Only update route advertisement
  1493. self.nsxpolicy.tier1.update_route_advertisement(
  1494. router_id,
  1495. static_routes=not new_enable_snat,
  1496. nat=actions['advertise_route_nat_flag'],
  1497. subnets=actions['advertise_route_connected_flag'])
  1498. if actions['add_snat_rules']:
  1499. # Add SNAT rules for all the subnets which are in different
  1500. # scope than the GW
  1501. gw_address_scope = self._get_network_address_scope(
  1502. context, router.gw_port.network_id)
  1503. for subnet in router_subnets:
  1504. self._add_subnet_snat_rule(context, router_id,
  1505. subnet, gw_address_scope,
  1506. newaddr)
  1507. if actions['add_no_dnat_rules']:
  1508. for subnet in router_subnets:
  1509. self._add_subnet_no_dnat_rule(context, router_id, subnet)
  1510. # always advertise ipv6 subnets if gateway is set
  1511. advertise_ipv6_subnets = True if info else False
  1512. self._update_router_advertisement_rules(router_id,
  1513. router_subnets,
  1514. advertise_ipv6_subnets)
  1515. if actions['remove_service_router']:
  1516. self.delete_service_router(router_id)
  1517. except nsx_lib_exc.NsxLibException as e:
  1518. # GW updates failed on the NSX. Rollback the change,
  1519. # unless it is during create or delete of a router
  1520. with excutils.save_and_reraise_exception():
  1521. if not called_from:
  1522. LOG.error("Rolling back router %s GW info update because "
  1523. "of NSX failure %s", router_id, e)
  1524. super(NsxPolicyPlugin, self)._update_router_gw_info(
  1525. context, router_id, orig_info, router=router)
  1526. def _update_router_advertisement_rules(self, router_id, subnets,
  1527. advertise_ipv6):
  1528. # There is no NAT for ipv6 - all connected ipv6 segments should be
  1529. # advertised
  1530. ipv6_cidrs = [s['cidr'] for s in subnets if s.get('ip_version') == 6]
  1531. if ipv6_cidrs and advertise_ipv6:
  1532. self.nsxpolicy.tier1.add_advertisement_rule(
  1533. router_id,
  1534. IPV6_ROUTER_ADV_RULE_NAME,
  1535. policy_constants.ADV_RULE_PERMIT,
  1536. policy_constants.ADV_RULE_OPERATOR_EQ,
  1537. [policy_constants.ADV_RULE_TIER1_CONNECTED],
  1538. ipv6_cidrs)
  1539. else:
  1540. self.nsxpolicy.tier1.remove_advertisement_rule(
  1541. router_id, IPV6_ROUTER_ADV_RULE_NAME)
  1542. def create_router(self, context, router):
  1543. r = router['router']
  1544. gw_info = self._extract_external_gw(context, router, is_extract=True)
  1545. # validate the availability zone, and get the AZ object
  1546. self._validate_obj_az_on_creation(context, r, 'router')
  1547. with db_api.CONTEXT_WRITER.using(context):
  1548. router = super(NsxPolicyPlugin, self).create_router(
  1549. context, router)
  1550. router_db = self._get_router(context, router['id'])
  1551. self._process_extra_attr_router_create(context, router_db, r)
  1552. router_name = utils.get_name_and_uuid(router['name'] or 'router',
  1553. router['id'])
  1554. tags = self.nsxpolicy.build_v3_tags_payload(
  1555. r, resource_type='os-neutron-router-id',
  1556. project_name=context.tenant_name)
  1557. try:
  1558. with policy_trans.NsxPolicyTransaction():
  1559. self.nsxpolicy.tier1.create_or_overwrite(
  1560. router_name, router['id'],
  1561. tier0=None,
  1562. ipv6_ndra_profile_id=NO_SLAAC_NDRA_PROFILE_ID,
  1563. tags=tags)
  1564. # Also create the empty locale-service as it must always exist
  1565. self.nsxpolicy.tier1.create_locale_service(router['id'])
  1566. #TODO(annak): narrow down the exception
  1567. except Exception as ex:
  1568. with excutils.save_and_reraise_exception():
  1569. LOG.error('Failed to create router %(id)s '
  1570. 'on NSX backend. Exception: %(e)s',
  1571. {'id': router['id'], 'e': ex})
  1572. self.delete_router(context, router['id'])
  1573. if gw_info and gw_info != const.ATTR_NOT_SPECIFIED:
  1574. try:
  1575. self._update_router_gw_info(context, router['id'], gw_info,
  1576. called_from="create")
  1577. except (db_exc.DBError, nsx_lib_exc.NsxLibException):
  1578. with excutils.save_and_reraise_exception():
  1579. LOG.error("Failed to set gateway info for router "
  1580. "being created: %s - removing router",
  1581. router['id'])
  1582. self.delete_router(context, router['id'])
  1583. LOG.info("Create router failed while setting external "
  1584. "gateway. Router:%s has been removed from "
  1585. "DB and backend",
  1586. router['id'])
  1587. return self.get_router(context, router['id'])
  1588. def delete_router(self, context, router_id):
  1589. gw_info = self._get_router_gw_info(context, router_id)
  1590. if gw_info:
  1591. try:
  1592. self._update_router_gw_info(context, router_id, {},
  1593. called_from="delete")
  1594. except nsx_lib_exc.NsxLibException as e:
  1595. LOG.error("Failed to remove router %s gw info before "
  1596. "deletion, but going on with the deletion anyway: "
  1597. "%s", router_id, e)
  1598. ret_val = super(NsxPolicyPlugin, self).delete_router(
  1599. context, router_id)
  1600. try:
  1601. self.nsxpolicy.tier1.delete_locale_service(router_id)
  1602. self.nsxpolicy.tier1.delete(router_id)
  1603. except nsx_lib_exc.ResourceNotFound:
  1604. # If the resource was not found on the backend do not worry about
  1605. # it. The conditions has already been logged, so there is no need
  1606. # to do further logging
  1607. pass
  1608. except nsx_lib_exc.ManagerError as e:
  1609. # If there is a failure in deleting the resource, fail the neutron
  1610. # operation even though the neutron object was already deleted.
  1611. # This way the user will be aware of zombie resources that may fail
  1612. # future actions.
  1613. msg = (_("Backend Tier1 deletion for neutron router %(id)s "
  1614. "failed. The object was however removed from the "
  1615. "Neutron database: %(e)s") % {'id': router_id, 'e': e})
  1616. nsx_exc.NsxPluginException(err_msg=msg)
  1617. return ret_val
  1618. def _get_static_route_id(self, route):
  1619. return "%s-%s" % (route['destination'].replace('/', '_'),
  1620. route['nexthop'])
  1621. def _add_static_routes(self, router_id, routes):
  1622. with policy_trans.NsxPolicyTransaction():
  1623. for route in routes:
  1624. dest = route['destination']
  1625. self.nsxpolicy.tier1_static_route.create_or_overwrite(
  1626. 'Static route for %s' % dest,
  1627. router_id,
  1628. static_route_id=self._get_static_route_id(route),
  1629. network=dest,
  1630. next_hop=route['nexthop'])
  1631. def _delete_static_routes(self, router_id, routes):
  1632. for route in routes:
  1633. self.nsxpolicy.tier1_static_route.delete(
  1634. router_id,
  1635. static_route_id=self._get_static_route_id(route))
  1636. @nsx_plugin_common.api_replay_mode_wrapper
  1637. def update_router(self, context, router_id, router):
  1638. gw_info = self._extract_external_gw(context, router, is_extract=False)
  1639. router_data = router['router']
  1640. self._assert_on_router_admin_state(router_data)
  1641. vpn_driver = None
  1642. if validators.is_attr_set(gw_info):
  1643. self._validate_update_router_gw(context, router_id, gw_info)
  1644. # VPNaaS need to be notified on router GW changes (there is
  1645. # currently no matching upstream registration for this)
  1646. vpn_plugin = directory.get_plugin(plugin_const.VPN)
  1647. if vpn_plugin:
  1648. vpn_driver = vpn_plugin.drivers[vpn_plugin.default_provider]
  1649. vpn_driver.validate_router_gw_info(context, router_id, gw_info)
  1650. routes_added = []
  1651. routes_removed = []
  1652. if 'routes' in router_data:
  1653. routes_added, routes_removed = self._get_static_routes_diff(
  1654. context, router_id, gw_info, router_data)
  1655. # Update the neutron router
  1656. updated_router = super(NsxPolicyPlugin, self).update_router(
  1657. context, router_id, router)
  1658. # Update the policy backend
  1659. try:
  1660. added_routes = removed_routes = False
  1661. # Updating name & description
  1662. if 'name' in router_data or 'description' in router_data:
  1663. router_name = utils.get_name_and_uuid(
  1664. updated_router.get('name') or 'router',
  1665. router_id)
  1666. self.nsxpolicy.tier1.update(
  1667. router_id, name=router_name,
  1668. description=updated_router.get('description', ''))
  1669. # Updating static routes
  1670. self._delete_static_routes(router_id, routes_removed)
  1671. removed_routes = True
  1672. self._add_static_routes(router_id, routes_added)
  1673. added_routes = True
  1674. except (nsx_lib_exc.ResourceNotFound, nsx_lib_exc.ManagerError):
  1675. with excutils.save_and_reraise_exception():
  1676. with db_api.CONTEXT_WRITER.using(context):
  1677. router_db = self._get_router(context, router_id)
  1678. router_db['status'] = const.NET_STATUS_ERROR
  1679. # return the static routes to the old state
  1680. if added_routes:
  1681. try:
  1682. self._delete_static_routes(router_id, routes_added)
  1683. except Exception as e:
  1684. LOG.error("Rollback router %s changes failed to "
  1685. "delete static routes: %s", router_id, e)
  1686. if removed_routes:
  1687. try:
  1688. self._add_static_routes(router_id, routes_removed)
  1689. except Exception as e:
  1690. LOG.error("Rollback router %s changes failed to add "
  1691. "static routes: %s", router_id, e)
  1692. if vpn_driver:
  1693. # Update vpn advertisement if GW was updated
  1694. vpn_driver.update_router_advertisement(context, router_id)
  1695. return updated_router
  1696. def _get_gateway_addr_from_subnet(self, subnet):
  1697. cidr_prefix = int(subnet['cidr'].split('/')[1])
  1698. return "%s/%s" % (subnet['gateway_ip'], cidr_prefix)
  1699. @nsx_plugin_common.api_replay_mode_wrapper
  1700. def add_router_interface(self, context, router_id, interface_info):
  1701. # NOTE: In dual stack case, neutron would create a separate interface
  1702. # for each IP version
  1703. # We only allow one subnet per IP version
  1704. subnet = self._get_interface_subnet(context, interface_info)
  1705. network_id = self._get_interface_network_id(context, interface_info,
  1706. subnet=subnet)
  1707. extern_net = self._network_is_external(context, network_id)
  1708. overlay_net = self._is_overlay_network(context, network_id)
  1709. router_db = self._get_router(context, router_id)
  1710. gw_network_id = (router_db.gw_port.network_id if router_db.gw_port
  1711. else None)
  1712. with locking.LockManager.get_lock(str(network_id)):
  1713. # disallow more than one subnets belong to same network being
  1714. # attached to routers
  1715. self._validate_multiple_subnets_routers(
  1716. context, router_id, network_id, subnet)
  1717. # A router interface cannot be an external network
  1718. if extern_net:
  1719. msg = _("An external network cannot be attached as "
  1720. "an interface to a router")
  1721. raise n_exc.InvalidInput(error_message=msg)
  1722. # Non overlay networks should be configured with a centralized
  1723. # router, which is allowed only if GW network is attached
  1724. if not overlay_net and not gw_network_id:
  1725. msg = _("A router attached to a VLAN backed network "
  1726. "must have an external network assigned")
  1727. raise n_exc.InvalidInput(error_message=msg)
  1728. # Interface subnets cannot overlap with the GW external subnet
  1729. self._validate_gw_overlap_interfaces(context, gw_network_id,
  1730. [network_id])
  1731. # Update the interface of the neutron router
  1732. info = super(NsxPolicyPlugin, self).add_router_interface(
  1733. context, router_id, interface_info)
  1734. try:
  1735. # If it is a no-snat router, interface address scope must be the
  1736. # same as the gateways
  1737. self._validate_interface_address_scope(context, router_db, subnet)
  1738. # Check GW & subnets TZ
  1739. tier0_uuid = self._get_tier0_uuid_by_router(
  1740. context.elevated(), router_db)
  1741. # Validate the TZ of the new subnet match the one of the router
  1742. self._validate_router_tz(context.elevated(), tier0_uuid, [subnet])
  1743. segment_id = self._get_network_nsx_segment_id(context, network_id)
  1744. rtr_subnets = self._load_router_subnet_cidrs_from_db(
  1745. context.elevated(), router_id)
  1746. if overlay_net:
  1747. # overlay interface
  1748. pol_subnets = []
  1749. for rtr_subnet in rtr_subnets:
  1750. # For dual stack, we allow one v4 and one v6
  1751. # subnet per network
  1752. if rtr_subnet['network_id'] == network_id:
  1753. gw_addr = self._get_gateway_addr_from_subnet(
  1754. rtr_subnet)
  1755. pol_subnets.append(
  1756. policy_defs.Subnet(gateway_address=gw_addr))
  1757. self.nsxpolicy.segment.update(segment_id,
  1758. tier1_id=router_id,
  1759. subnets=pol_subnets)
  1760. # will update the router only if needed
  1761. self._update_slaac_on_router(context, router_id,
  1762. subnet, rtr_subnets)
  1763. else:
  1764. # Vlan interface
  1765. pol_subnets = []
  1766. for rtr_subnet in rtr_subnets:
  1767. if rtr_subnet['network_id'] == network_id:
  1768. prefix_len = int(rtr_subnet['cidr'].split('/')[1])
  1769. pol_subnets.append(policy_defs.InterfaceSubnet(
  1770. ip_addresses=[rtr_subnet['gateway_ip']],
  1771. prefix_len=prefix_len))
  1772. slaac_subnet = (subnet.get('ipv6_address_mode') == 'slaac')
  1773. ndra_profile_id = (SLAAC_NDRA_PROFILE_ID if slaac_subnet
  1774. else NO_SLAAC_NDRA_PROFILE_ID)
  1775. self.nsxpolicy.tier1.add_segment_interface(
  1776. router_id, segment_id,
  1777. segment_id, pol_subnets,
  1778. ndra_profile_id)
  1779. # add the SNAT/NO_DNAT rules for this interface
  1780. if router_db.enable_snat and gw_network_id:
  1781. if router_db.gw_port.get('fixed_ips'):
  1782. gw_ip = router_db.gw_port['fixed_ips'][0]['ip_address']
  1783. gw_address_scope = self._get_network_address_scope(
  1784. context, gw_network_id)
  1785. self._add_subnet_snat_rule(
  1786. context, router_id,
  1787. subnet, gw_address_scope, gw_ip)
  1788. self._add_subnet_no_dnat_rule(context, router_id, subnet)
  1789. if subnet.get('ip_version') == 6 and gw_network_id:
  1790. # if this is an ipv6 subnet and router has GW,
  1791. # we need to add advertisement rule
  1792. self._update_router_advertisement_rules(
  1793. router_id, rtr_subnets, True)
  1794. # update firewall rules
  1795. self.update_router_firewall(context, router_id, router_db)
  1796. except Exception as ex:
  1797. with excutils.save_and_reraise_exception():
  1798. LOG.error('Failed to create router interface for network '
  1799. '%(id)s on NSX backend. Exception: %(e)s',
  1800. {'id': network_id, 'e': ex})
  1801. self.remove_router_interface(
  1802. context, router_id, interface_info)
  1803. return info
  1804. def remove_router_interface(self, context, router_id, interface_info):
  1805. # find the subnet - it is need for removing the SNAT rule
  1806. subnet = subnet_id = None
  1807. if 'port_id' in interface_info:
  1808. port_id = interface_info['port_id']
  1809. port = self._get_port(context, port_id)
  1810. if port.get('fixed_ips'):
  1811. subnet_id = port['fixed_ips'][0]['subnet_id']
  1812. elif 'subnet_id' in interface_info:
  1813. subnet_id = interface_info['subnet_id']
  1814. if subnet_id:
  1815. subnet = self.get_subnet(context, subnet_id)
  1816. # Update the neutron router first
  1817. info = super(NsxPolicyPlugin, self).remove_router_interface(
  1818. context, router_id, interface_info)
  1819. network_id = info['network_id']
  1820. overlay_net = self._is_overlay_network(context, network_id)
  1821. segment_id = self._get_network_nsx_segment_id(context, network_id)
  1822. rtr_subnets = self._load_router_subnet_cidrs_from_db(
  1823. context.elevated(), router_id)
  1824. try:
  1825. if overlay_net:
  1826. # Remove the tier1 router from this segment on the NSX
  1827. pol_subnets = []
  1828. for rtr_subnet in rtr_subnets:
  1829. # For dual stack, we allow one v4 and one v6
  1830. # subnet per network
  1831. if rtr_subnet['network_id'] == network_id:
  1832. gw_addr = self._get_gateway_addr_from_subnet(
  1833. rtr_subnet)
  1834. pol_subnets.append(
  1835. policy_defs.Subnet(gateway_address=gw_addr))
  1836. if pol_subnets:
  1837. self.nsxpolicy.segment.update(segment_id,
  1838. tier1_id=router_id,
  1839. subnets=pol_subnets)
  1840. else:
  1841. self.nsxpolicy.segment.remove_connectivity_and_subnets(
  1842. segment_id)
  1843. # will update the router only if needed
  1844. self._update_slaac_on_router(context, router_id,
  1845. subnet, rtr_subnets, delete=True)
  1846. else:
  1847. # VLAN interface
  1848. pol_subnets = []
  1849. for rtr_subnet in rtr_subnets:
  1850. if rtr_subnet['network_id'] == network_id:
  1851. prefix_len = int(rtr_subnet['cidr'].split('/')[1])
  1852. pol_subnets.append(policy_defs.InterfaceSubnet(
  1853. ip_addresses=[rtr_subnet['gateway_ip']],
  1854. prefix_len=prefix_len))
  1855. if pol_subnets:
  1856. # This will update segment interface
  1857. self.nsxpolicy.tier1.add_segment_interface(
  1858. router_id, segment_id,
  1859. segment_id, pol_subnets)
  1860. else:
  1861. self.nsxpolicy.tier1.remove_segment_interface(
  1862. router_id, segment_id)
  1863. # try to delete the SNAT/NO_DNAT rules of this subnet
  1864. router_db = self._get_router(context, router_id)
  1865. if (subnet and router_db.gw_port and router_db.enable_snat and
  1866. subnet['ip_version'] == 4):
  1867. self._del_subnet_snat_rule(router_id, subnet)
  1868. self._del_subnet_no_dnat_rule(router_id, subnet)
  1869. if subnet and subnet.get('ip_version') == 6 and router_db.gw_port:
  1870. # if this is an ipv6 subnet and router has GW,
  1871. # we need to remove advertisement rule
  1872. self._update_router_advertisement_rules(
  1873. router_id, rtr_subnets, True)
  1874. # update firewall rules
  1875. self.update_router_firewall(context, router_id, router_db)
  1876. except nsx_lib_exc.ManagerError as e:
  1877. # If there is a failure in deleting the resource, fail the neutron
  1878. # operation even though the neutron object was already deleted.
  1879. # This way the user will be aware of zombie resources that may fail
  1880. # future actions.
  1881. # TODO(asarfaty): Handle specific errors
  1882. msg = (_('Failed to remove router interface for network '
  1883. '%(id)s on NSX backend. Exception: %(e)s') %
  1884. {'id': network_id, 'e': e})
  1885. raise nsx_exc.NsxPluginException(err_msg=msg)
  1886. return info
  1887. def _get_fip_snat_rule_id(self, fip_id):
  1888. return 'S-' + fip_id
  1889. def _get_fip_dnat_rule_id(self, fip_id):
  1890. return 'D-' + fip_id
  1891. def _add_fip_nat_rules(self, tier1_id, fip_id, ext_ip, int_ip):
  1892. #TODO(asarfaty): Add policy transactions here
  1893. self.nsxpolicy.tier1_nat_rule.create_or_overwrite(
  1894. 'snat for fip %s' % fip_id,
  1895. tier1_id,
  1896. nat_rule_id=self._get_fip_snat_rule_id(fip_id),
  1897. action=policy_constants.NAT_ACTION_SNAT,
  1898. translated_network=ext_ip,
  1899. source_network=int_ip,
  1900. sequence_number=NAT_RULE_PRIORITY_FIP,
  1901. firewall_match=policy_constants.NAT_FIREWALL_MATCH_INTERNAL)
  1902. self.nsxpolicy.tier1_nat_rule.create_or_overwrite(
  1903. 'dnat for fip %s' % fip_id,
  1904. tier1_id,
  1905. nat_rule_id=self._get_fip_dnat_rule_id(fip_id),
  1906. action=policy_constants.NAT_ACTION_DNAT,
  1907. translated_network=int_ip,
  1908. destination_network=ext_ip,
  1909. sequence_number=NAT_RULE_PRIORITY_FIP,
  1910. firewall_match=policy_constants.NAT_FIREWALL_MATCH_INTERNAL)
  1911. def _delete_fip_nat_rules(self, tier1_id, fip_id):
  1912. #TODO(asarfaty): Add policy transactions here
  1913. self.nsxpolicy.tier1_nat_rule.delete(
  1914. tier1_id,
  1915. nat_rule_id=self._get_fip_snat_rule_id(fip_id))
  1916. self.nsxpolicy.tier1_nat_rule.delete(
  1917. tier1_id,
  1918. nat_rule_id=self._get_fip_dnat_rule_id(fip_id))
  1919. def _update_lb_vip(self, port, vip_address):
  1920. # update the load balancer virtual server's VIP with
  1921. # floating ip, but don't add NAT rules
  1922. device_id = port['device_id']
  1923. if device_id.startswith(oct_const.DEVICE_ID_PREFIX):
  1924. device_id = device_id[len(oct_const.DEVICE_ID_PREFIX):]
  1925. tags_to_search = [{'scope': 'os-lbaas-lb-id', 'tag': device_id}]
  1926. vs_client = self.nsxpolicy.load_balancer.virtual_server
  1927. vs_list = self.nsxpolicy.search_by_tags(
  1928. tags_to_search, vs_client.entry_def.resource_type()
  1929. )['results']
  1930. for vs in vs_list:
  1931. vs_client.update(vs['id'], ip_address=vip_address)
  1932. def create_floatingip(self, context, floatingip):
  1933. # First do some validations
  1934. fip_data = floatingip['floatingip']
  1935. port_id = fip_data.get('port_id')
  1936. if port_id:
  1937. port_data = self.get_port(context, port_id)
  1938. self._assert_on_assoc_floatingip_to_special_ports(
  1939. fip_data, port_data)
  1940. new_fip = self._create_floating_ip_wrapper(context, floatingip)
  1941. router_id = new_fip['router_id']
  1942. if not router_id:
  1943. return new_fip
  1944. if port_id:
  1945. device_owner = port_data.get('device_owner')
  1946. fip_address = new_fip['floating_ip_address']
  1947. if (device_owner == const.DEVICE_OWNER_LOADBALANCERV2 or
  1948. device_owner == oct_const.DEVICE_OWNER_OCTAVIA or
  1949. device_owner == lb_const.VMWARE_LB_VIP_OWNER):
  1950. try:
  1951. self._update_lb_vip(port_data, fip_address)
  1952. except nsx_lib_exc.ManagerError:
  1953. with excutils.save_and_reraise_exception():
  1954. super(NsxPolicyPlugin, self).delete_floatingip(
  1955. context, new_fip['id'])
  1956. return new_fip
  1957. try:
  1958. self._add_fip_nat_rules(
  1959. router_id, new_fip['id'],
  1960. new_fip['floating_ip_address'],
  1961. new_fip['fixed_ip_address'])
  1962. except nsx_lib_exc.ManagerError:
  1963. with excutils.save_and_reraise_exception():
  1964. self.delete_floatingip(context, new_fip['id'])
  1965. return new_fip
  1966. def delete_floatingip(self, context, fip_id):
  1967. fip = self.get_floatingip(context, fip_id)
  1968. router_id = fip['router_id']
  1969. port_id = fip['port_id']
  1970. is_lb_port = False
  1971. if port_id:
  1972. port_data = self.get_port(context, port_id)
  1973. device_owner = port_data.get('device_owner')
  1974. fixed_ip_address = fip['fixed_ip_address']
  1975. if (device_owner == const.DEVICE_OWNER_LOADBALANCERV2 or
  1976. device_owner == oct_const.DEVICE_OWNER_OCTAVIA or
  1977. device_owner == lb_const.VMWARE_LB_VIP_OWNER):
  1978. # If the port is LB VIP port, after deleting the FIP,
  1979. # update the virtual server VIP back to fixed IP.
  1980. is_lb_port = True
  1981. try:
  1982. self._update_lb_vip(port_data, fixed_ip_address)
  1983. except nsx_lib_exc.ManagerError as e:
  1984. LOG.error("Exception when updating vip ip_address"
  1985. "on vip_port %(port)s: %(err)s",
  1986. {'port': port_id, 'err': e})
  1987. if router_id and not is_lb_port:
  1988. self._delete_fip_nat_rules(router_id, fip_id)
  1989. super(NsxPolicyPlugin, self).delete_floatingip(context, fip_id)
  1990. def update_floatingip(self, context, fip_id, floatingip):
  1991. fip_data = floatingip['floatingip']
  1992. old_fip = self.get_floatingip(context, fip_id)
  1993. old_port_id = old_fip['port_id']
  1994. new_status = (const.FLOATINGIP_STATUS_ACTIVE
  1995. if fip_data.get('port_id')
  1996. else const.FLOATINGIP_STATUS_DOWN)
  1997. updated_port_id = fip_data.get('port_id')
  1998. if updated_port_id:
  1999. updated_port_data = self.get_port(context, updated_port_id)
  2000. self._assert_on_assoc_floatingip_to_special_ports(
  2001. fip_data, updated_port_data)
  2002. new_fip = super(NsxPolicyPlugin, self).update_floatingip(
  2003. context, fip_id, floatingip)
  2004. router_id = new_fip['router_id']
  2005. new_port_id = new_fip['port_id']
  2006. # Delete old configuration NAT / vip
  2007. is_lb_port = False
  2008. if old_port_id:
  2009. old_port_data = self.get_port(context, old_port_id)
  2010. old_device_owner = old_port_data['device_owner']
  2011. old_fixed_ip = old_fip['fixed_ip_address']
  2012. if (old_device_owner == const.DEVICE_OWNER_LOADBALANCERV2 or
  2013. old_device_owner == oct_const.DEVICE_OWNER_OCTAVIA or
  2014. old_device_owner == lb_const.VMWARE_LB_VIP_OWNER):
  2015. # If the port is LB VIP port, after deleting the FIP,
  2016. # update the virtual server VIP back to fixed IP.
  2017. is_lb_port = True
  2018. self._update_lb_vip(old_port_data, old_fixed_ip)
  2019. if (not is_lb_port and old_fip['router_id'] and
  2020. (not router_id or old_fip['router_id'] != router_id)):
  2021. # Delete the old rules (if the router did not change - rewriting
  2022. # the rules with _add_fip_nat_rules is enough)
  2023. self._delete_fip_nat_rules(old_fip['router_id'], fip_id)
  2024. # Update LB VIP if the new port is LB port
  2025. is_lb_port = False
  2026. if new_port_id:
  2027. new_port_data = self.get_port(context, new_port_id)
  2028. new_dev_own = new_port_data['device_owner']
  2029. new_fip_address = new_fip['floating_ip_address']
  2030. if (new_dev_own == const.DEVICE_OWNER_LOADBALANCERV2 or
  2031. new_dev_own == oct_const.DEVICE_OWNER_OCTAVIA or
  2032. new_dev_own == lb_const.VMWARE_LB_VIP_OWNER):
  2033. is_lb_port = True
  2034. self._update_lb_vip(new_port_data, new_fip_address)
  2035. if router_id and not is_lb_port:
  2036. self._add_fip_nat_rules(
  2037. router_id, new_fip['id'],
  2038. new_fip['floating_ip_address'],
  2039. new_fip['fixed_ip_address'])
  2040. if new_fip['status'] != new_status:
  2041. new_fip['status'] = new_status
  2042. self.update_floatingip_status(context, fip_id, new_status)
  2043. return new_fip
  2044. def disassociate_floatingips(self, context, port_id):
  2045. fip_qry = context.session.query(l3_db_models.FloatingIP)
  2046. fip_dbs = fip_qry.filter_by(fixed_port_id=port_id)
  2047. for fip_db in fip_dbs:
  2048. if not fip_db.router_id:
  2049. continue
  2050. if fip_db.router_id:
  2051. # Delete the old rules
  2052. self._delete_fip_nat_rules(fip_db.router_id, fip_db.id)
  2053. self.update_floatingip_status(context, fip_db.id,
  2054. const.FLOATINGIP_STATUS_DOWN)
  2055. super(NsxPolicyPlugin, self).disassociate_floatingips(
  2056. context, port_id, do_notify=False)
  2057. def _prepare_default_rules(self):
  2058. """Create a default group & communication map in the default domain"""
  2059. # Run this code only on one worker at the time
  2060. with locking.LockManager.get_lock('nsx_p_prepare_default_rules'):
  2061. # Return if the objects were already created
  2062. try:
  2063. self.nsxpolicy.comm_map.get(NSX_P_GLOBAL_DOMAIN_ID,
  2064. NSX_P_DEFAULT_SECTION)
  2065. self.nsxpolicy.group.get(NSX_P_GLOBAL_DOMAIN_ID,
  2066. NSX_P_DEFAULT_GROUP)
  2067. except nsx_lib_exc.ResourceNotFound:
  2068. LOG.info("Going to create default group & "
  2069. "communication map under the default domain")
  2070. else:
  2071. LOG.debug("Verified default group already exist")
  2072. return
  2073. # Create the default group membership criteria to match all neutron
  2074. # ports by scope (and no tag)
  2075. scope_and_tag = "%s|" % (NSX_P_PORT_RESOURCE_TYPE)
  2076. conditions = [self.nsxpolicy.group.build_condition(
  2077. cond_val=scope_and_tag,
  2078. cond_key=policy_constants.CONDITION_KEY_TAG,
  2079. cond_member_type=policy_constants.CONDITION_MEMBER_PORT)]
  2080. # Create the default OpenStack group
  2081. # (This will not fail if the group already exists)
  2082. try:
  2083. self.nsxpolicy.group.create_or_overwrite_with_conditions(
  2084. name=NSX_P_DEFAULT_GROUP,
  2085. domain_id=NSX_P_GLOBAL_DOMAIN_ID,
  2086. group_id=NSX_P_DEFAULT_GROUP,
  2087. description=NSX_P_DEFAULT_GROUP_DESC,
  2088. conditions=conditions)
  2089. except Exception as e:
  2090. msg = (_("Failed to create NSX default group: %(e)s") % {
  2091. 'e': e})
  2092. raise nsx_exc.NsxPluginException(err_msg=msg)
  2093. # create default section and rules
  2094. logged = cfg.CONF.nsx_p.log_security_groups_blocked_traffic
  2095. scope = [self.nsxpolicy.group.get_path(
  2096. NSX_P_GLOBAL_DOMAIN_ID, NSX_P_DEFAULT_GROUP)]
  2097. rule_id = 1
  2098. dhcp_client_rule = self.nsxpolicy.comm_map.build_entry(
  2099. 'DHCP Reply', NSX_P_GLOBAL_DOMAIN_ID,
  2100. NSX_P_DEFAULT_SECTION,
  2101. rule_id, sequence_number=rule_id,
  2102. service_ids=['DHCP-Client'],
  2103. action=policy_constants.ACTION_ALLOW,
  2104. scope=scope,
  2105. direction=nsxlib_consts.IN,
  2106. logged=logged)
  2107. rule_id += 1
  2108. dhcp_server_rule = self.nsxpolicy.comm_map.build_entry(
  2109. 'DHCP Request', NSX_P_GLOBAL_DOMAIN_ID,
  2110. NSX_P_DEFAULT_SECTION,
  2111. rule_id, sequence_number=rule_id,
  2112. service_ids=['DHCP-Server'],
  2113. action=policy_constants.ACTION_ALLOW,
  2114. scope=scope,
  2115. direction=nsxlib_consts.OUT,
  2116. logged=logged)
  2117. rule_id += 1
  2118. nd_rule = self.nsxpolicy.comm_map.build_entry(
  2119. 'IPv6 Neighbor Discovery', NSX_P_GLOBAL_DOMAIN_ID,
  2120. NSX_P_DEFAULT_SECTION,
  2121. rule_id, sequence_number=rule_id,
  2122. service_ids=['IPv6-ICMP_Neighbor_Solicitation',
  2123. 'IPv6-ICMP_Neighbor_Advertisement',
  2124. 'IPv6-ICMP_Version_2_Multicast_Listener',
  2125. 'IPv6-ICMP_Multicast_Listener_Query',
  2126. 'IPv6-ICMP_Multicast_Listener_Done',
  2127. 'IPv6-ICMP_Multicast_Listener_Report',
  2128. IPV6_RA_SERVICE],
  2129. action=policy_constants.ACTION_ALLOW,
  2130. ip_protocol=nsxlib_consts.IPV6,
  2131. scope=scope,
  2132. direction=nsxlib_consts.IN_OUT,
  2133. logged=logged)
  2134. rule_id += 1
  2135. block_rule = self.nsxpolicy.comm_map.build_entry(
  2136. 'Block All', NSX_P_GLOBAL_DOMAIN_ID,
  2137. NSX_P_DEFAULT_SECTION,
  2138. rule_id, sequence_number=rule_id, service_ids=None,
  2139. action=policy_constants.ACTION_DENY,
  2140. scope=scope,
  2141. direction=nsxlib_consts.IN_OUT,
  2142. logged=logged)
  2143. rules = [dhcp_client_rule, dhcp_server_rule, nd_rule, block_rule]
  2144. try:
  2145. # This will not fail if the map already exists
  2146. self.nsxpolicy.comm_map.create_with_entries(
  2147. name=NSX_P_DEFAULT_SECTION,
  2148. domain_id=NSX_P_GLOBAL_DOMAIN_ID,
  2149. map_id=NSX_P_DEFAULT_SECTION,
  2150. description=NSX_P_DEFAULT_SECTION_DESC,
  2151. category=NSX_P_DEFAULT_SECTION_CATEGORY,
  2152. entries=rules)
  2153. except Exception as e:
  2154. msg = (_("Failed to create NSX default communication map: "
  2155. "%(e)s") % {'e': e})
  2156. raise nsx_exc.NsxPluginException(err_msg=msg)
  2157. def _prepare_exclude_list_group(self):
  2158. try:
  2159. self.nsxpolicy.group.get(NSX_P_GLOBAL_DOMAIN_ID,
  2160. NSX_P_EXCLUDE_LIST_GROUP)
  2161. except nsx_lib_exc.ResourceNotFound:
  2162. LOG.info("Going to create exclude list group")
  2163. else:
  2164. LOG.debug("Verified exclude list group already exists")
  2165. return
  2166. # Create the group membership criteria to match excluded neutron
  2167. # ports by scope and tag
  2168. scope_and_tag = "%s|%s" % (security.PORT_SG_SCOPE,
  2169. NSX_P_EXCLUDE_LIST_TAG)
  2170. conditions = [self.nsxpolicy.group.build_condition(
  2171. cond_val=scope_and_tag,
  2172. cond_key=policy_constants.CONDITION_KEY_TAG,
  2173. cond_member_type=policy_constants.CONDITION_MEMBER_PORT)]
  2174. # Create the exclude list group
  2175. # (This will not fail if the group already exists)
  2176. try:
  2177. self.nsxpolicy.group.create_or_overwrite_with_conditions(
  2178. name=NSX_P_EXCLUDE_LIST_GROUP,
  2179. domain_id=NSX_P_GLOBAL_DOMAIN_ID,
  2180. group_id=NSX_P_EXCLUDE_LIST_GROUP,
  2181. conditions=conditions)
  2182. except Exception as e:
  2183. msg = (_("Failed to create NSX exclude list group: %(e)s") % {
  2184. 'e': e})
  2185. raise nsx_exc.NsxPluginException(err_msg=msg)
  2186. def _add_exclude_list_group(self):
  2187. member = self.nsxpolicy.group.get_path(
  2188. domain_id=NSX_P_GLOBAL_DOMAIN_ID,
  2189. group_id=NSX_P_EXCLUDE_LIST_GROUP)
  2190. exclude_list = self.nsxpolicy.exclude_list.get()
  2191. if member in exclude_list['members']:
  2192. LOG.debug("Verified that group %s was already added to the "
  2193. "NSX exclude list", member)
  2194. return
  2195. LOG.info("Going to add group %s to the NSX exclude list", member)
  2196. members = exclude_list['members']
  2197. members.append(member)
  2198. try:
  2199. self.nsxpolicy.exclude_list.create_or_overwrite(members=members)
  2200. except Exception as e:
  2201. msg = (_("Failed to add group to the NSX exclude list: %(e)s") % {
  2202. 'e': e})
  2203. raise nsx_exc.NsxPluginException(err_msg=msg)
  2204. def _prepare_exclude_list(self):
  2205. """Create exclude list for ports without port security
  2206. Create a group for excluded ports and add it to the NSX exclude list
  2207. """
  2208. # Run this code only on one worker at the time
  2209. with locking.LockManager.get_lock('nsx_p_prepare_exclude_list'):
  2210. self._prepare_exclude_list_group()
  2211. self._add_exclude_list_group()
  2212. def _create_security_group_backend_resources(self, context, secgroup,
  2213. entries):
  2214. """Create communication map (=section) and group (=NS group)
  2215. Both will have the security group id as their NSX id.
  2216. """
  2217. sg_id = secgroup['id']
  2218. tags = self.nsxpolicy.build_v3_tags_payload(
  2219. secgroup, resource_type='os-neutron-secg-id',
  2220. project_name=secgroup.get('tenant_id'))
  2221. nsx_name = utils.get_name_and_uuid(secgroup['name'] or 'securitygroup',
  2222. sg_id)
  2223. # Create the groups membership criteria for ports by scope & tag
  2224. scope_and_tag = "%s|%s" % (NSX_P_SECURITY_GROUP_TAG, sg_id)
  2225. condition = self.nsxpolicy.group.build_condition(
  2226. cond_val=scope_and_tag,
  2227. cond_key=policy_constants.CONDITION_KEY_TAG,
  2228. cond_member_type=policy_constants.CONDITION_MEMBER_PORT)
  2229. category = NSX_P_REGULAR_SECTION_CATEGORY
  2230. if secgroup.get(provider_sg.PROVIDER) is True:
  2231. category = NSX_P_PROVIDER_SECTION_CATEGORY
  2232. try:
  2233. with policy_trans.NsxPolicyTransaction():
  2234. # Create the group
  2235. self.nsxpolicy.group.create_or_overwrite_with_conditions(
  2236. nsx_name, NSX_P_GLOBAL_DOMAIN_ID, group_id=sg_id,
  2237. description=secgroup.get('description'),
  2238. conditions=[condition], tags=tags)
  2239. # create the communication map (=section) and entries (=rules)
  2240. self.nsxpolicy.comm_map.create_or_overwrite_map_only(
  2241. nsx_name, NSX_P_GLOBAL_DOMAIN_ID, map_id=sg_id,
  2242. description=secgroup.get('description'),
  2243. tags=tags, category=category)
  2244. for entry in entries:
  2245. self.nsxpolicy.comm_map.create_entry_from_def(entry)
  2246. except Exception as e:
  2247. msg = (_("Failed to create NSX resources for SG %(sg)s: "
  2248. "%(e)s") % {'sg': sg_id, 'e': e})
  2249. raise nsx_exc.NsxPluginException(err_msg=msg)
  2250. def _get_rule_ip_protocol(self, sg_rule):
  2251. ethertype = sg_rule.get('ethertype')
  2252. if ethertype == const.IPv4:
  2253. return nsxlib_consts.IPV4
  2254. if ethertype == const.IPv6:
  2255. return nsxlib_consts.IPV6
  2256. return nsxlib_consts.IPV4_IPV6
  2257. def _get_rule_service_id(self, context, sg_rule, tags):
  2258. """Return the NSX Policy service id matching the SG rule"""
  2259. srv_id = None
  2260. l4_protocol = nsxlib_utils.get_l4_protocol_name(sg_rule['protocol'])
  2261. srv_name = 'Service for OS rule %s' % sg_rule['id']
  2262. if l4_protocol in [nsxlib_consts.TCP, nsxlib_consts.UDP]:
  2263. # If port_range_min is not specified then we assume all ports are
  2264. # matched, relying on neutron to perform validation.
  2265. if sg_rule['port_range_min'] is None:
  2266. destination_ports = []
  2267. elif sg_rule['port_range_min'] != sg_rule['port_range_max']:
  2268. # NSX API requires a non-empty range (e.g - '22-23')
  2269. destination_ports = ['%(port_range_min)s-%(port_range_max)s'
  2270. % sg_rule]
  2271. else:
  2272. destination_ports = ['%(port_range_min)s' % sg_rule]
  2273. srv_id = self.nsxpolicy.service.create_or_overwrite(
  2274. srv_name, service_id=sg_rule['id'],
  2275. description=sg_rule.get('description'),
  2276. protocol=l4_protocol,
  2277. dest_ports=destination_ports,
  2278. tags=tags)
  2279. elif l4_protocol in [nsxlib_consts.ICMPV4, nsxlib_consts.ICMPV6]:
  2280. # Validate the icmp type & code
  2281. version = 4 if l4_protocol == nsxlib_consts.ICMPV4 else 6
  2282. icmp_type = sg_rule['port_range_min']
  2283. icmp_code = sg_rule['port_range_max']
  2284. nsxlib_utils.validate_icmp_params(
  2285. icmp_type, icmp_code, icmp_version=version, strict=True)
  2286. srv_id = self.nsxpolicy.icmp_service.create_or_overwrite(
  2287. srv_name, service_id=sg_rule['id'],
  2288. description=sg_rule.get('description'),
  2289. version=version,
  2290. icmp_type=icmp_type,
  2291. icmp_code=icmp_code,
  2292. tags=tags)
  2293. elif l4_protocol:
  2294. srv_id = self.nsxpolicy.ip_protocol_service.create_or_overwrite(
  2295. srv_name, service_id=sg_rule['id'],
  2296. description=sg_rule.get('description'),
  2297. protocol_number=l4_protocol,
  2298. tags=tags)
  2299. return srv_id
  2300. def _get_sg_rule_remote_ip_group_id(self, sg_rule):
  2301. return '%s_remote_group' % sg_rule['id']
  2302. def _get_sg_rule_local_ip_group_id(self, sg_rule):
  2303. return '%s_local_group' % sg_rule['id']
  2304. def _create_security_group_backend_rule(self, context, map_id,
  2305. sg_rule, secgroup_logging,
  2306. is_provider_sg=False):
  2307. """Create backend resources for a DFW rule
  2308. All rule resources (service, groups) will be created
  2309. The rule itself will be created if create_rule=True.
  2310. Else this method will return the rule entry structure for future use.
  2311. """
  2312. # The id of the map and group is the same as the security group id
  2313. this_group_id = map_id
  2314. # There is no rule name in neutron. Using ID instead
  2315. nsx_name = sg_rule['id']
  2316. direction = (nsxlib_consts.IN if sg_rule.get('direction') == 'ingress'
  2317. else nsxlib_consts.OUT)
  2318. self._fix_sg_rule_dict_ips(sg_rule)
  2319. source = None
  2320. destination = this_group_id
  2321. tags = self.nsxpolicy.build_v3_tags_payload(
  2322. sg_rule, resource_type='os-neutron-secgr-id',
  2323. project_name=sg_rule.get('tenant_id'))
  2324. if sg_rule.get('remote_group_id'):
  2325. # This is the ID of a security group that already exists,
  2326. # so it should be known to the policy manager
  2327. source = sg_rule.get('remote_group_id')
  2328. elif sg_rule.get('remote_ip_prefix'):
  2329. # Create a group for the remote IPs
  2330. remote_ip = sg_rule['remote_ip_prefix']
  2331. remote_group_id = self._get_sg_rule_remote_ip_group_id(sg_rule)
  2332. expr = self.nsxpolicy.group.build_ip_address_expression(
  2333. [remote_ip])
  2334. self.nsxpolicy.group.create_or_overwrite_with_conditions(
  2335. remote_group_id, NSX_P_GLOBAL_DOMAIN_ID,
  2336. group_id=remote_group_id,
  2337. description='%s for OS rule %s' % (remote_ip, sg_rule['id']),
  2338. conditions=[expr], tags=tags)
  2339. source = remote_group_id
  2340. if sg_rule.get(sg_prefix.LOCAL_IP_PREFIX):
  2341. # Create a group for the local ips
  2342. local_ip = sg_rule[sg_prefix.LOCAL_IP_PREFIX]
  2343. local_group_id = self._get_sg_rule_local_ip_group_id(sg_rule)
  2344. expr = self.nsxpolicy.group.build_ip_address_expression(
  2345. [local_ip])
  2346. self.nsxpolicy.group.create_or_overwrite_with_conditions(
  2347. local_group_id, NSX_P_GLOBAL_DOMAIN_ID,
  2348. group_id=local_group_id,
  2349. description='%s for OS rule %s' % (local_ip, sg_rule['id']),
  2350. conditions=[expr], tags=tags)
  2351. destination = local_group_id
  2352. if direction == nsxlib_consts.OUT:
  2353. # Swap source and destination
  2354. source, destination = destination, source
  2355. service = self._get_rule_service_id(context, sg_rule, tags)
  2356. ip_protocol = self._get_rule_ip_protocol(sg_rule)
  2357. logging = (cfg.CONF.nsx_p.log_security_groups_allowed_traffic or
  2358. secgroup_logging)
  2359. scope = [self.nsxpolicy.group.get_path(NSX_P_GLOBAL_DOMAIN_ID,
  2360. this_group_id)]
  2361. action = (policy_constants.ACTION_DENY if is_provider_sg
  2362. else policy_constants.ACTION_ALLOW)
  2363. # Just return the rule entry without creating it
  2364. rule_entry = self.nsxpolicy.comm_map.build_entry(
  2365. nsx_name, NSX_P_GLOBAL_DOMAIN_ID,
  2366. map_id, entry_id=sg_rule['id'],
  2367. description=sg_rule.get('description'),
  2368. service_ids=[service] if service else None,
  2369. ip_protocol=ip_protocol,
  2370. action=action,
  2371. source_groups=[source] if source else None,
  2372. dest_groups=[destination] if destination else None,
  2373. scope=scope,
  2374. tag=sg_rule.get('project_id'),
  2375. direction=direction, logged=logging)
  2376. return rule_entry
  2377. def create_security_group(self, context, security_group, default_sg=False):
  2378. secgroup = security_group['security_group']
  2379. # Make sure the ID is initialized, as it is used for the backend
  2380. # objects too
  2381. secgroup['id'] = secgroup.get('id') or uuidutils.generate_uuid()
  2382. project_id = secgroup['tenant_id']
  2383. if not default_sg:
  2384. self._ensure_default_security_group(context, project_id)
  2385. # create the Neutron SG
  2386. with db_api.CONTEXT_WRITER.using(context):
  2387. if secgroup.get(provider_sg.PROVIDER) is True:
  2388. secgroup_db = self.create_provider_security_group(
  2389. context, security_group)
  2390. else:
  2391. secgroup_db = (
  2392. super(NsxPolicyPlugin, self).create_security_group(
  2393. context, security_group, default_sg))
  2394. self._process_security_group_properties_create(context,
  2395. secgroup_db,
  2396. secgroup,
  2397. default_sg)
  2398. if cfg.CONF.api_replay_mode:
  2399. self._handle_api_replay_default_sg(context, secgroup_db)
  2400. try:
  2401. # create all the rule entries
  2402. sg_rules = secgroup_db['security_group_rules']
  2403. secgroup_logging = secgroup.get(sg_logging.LOGGING, False)
  2404. backend_rules = []
  2405. with policy_trans.NsxPolicyTransaction():
  2406. # Create all the rules resources in a single transaction
  2407. for sg_rule in sg_rules:
  2408. rule_entry = self._create_security_group_backend_rule(
  2409. context, secgroup_db['id'], sg_rule,
  2410. secgroup_logging)
  2411. backend_rules.append(rule_entry)
  2412. # Create Group & communication map on the NSX
  2413. self._create_security_group_backend_resources(
  2414. context, secgroup, backend_rules)
  2415. except Exception as e:
  2416. with excutils.save_and_reraise_exception():
  2417. LOG.exception("Failed to create backend SG rules "
  2418. "for security-group %(name)s (%(id)s), "
  2419. "rolling back changes. Error: %(e)s",
  2420. {'name': secgroup_db['name'],
  2421. 'id': secgroup_db['id'],
  2422. 'e': e})
  2423. # rollback SG creation (which will also delete the backend
  2424. # objects)
  2425. super(NsxPolicyPlugin, self).delete_security_group(
  2426. context, secgroup['id'])
  2427. return secgroup_db
  2428. def update_security_group(self, context, sg_id, security_group):
  2429. self._prevent_non_admin_edit_provider_sg(context, sg_id)
  2430. sg_data = security_group['security_group']
  2431. # update the neutron security group
  2432. with db_api.CONTEXT_WRITER.using(context):
  2433. secgroup_res = super(NsxPolicyPlugin, self).update_security_group(
  2434. context, sg_id, security_group)
  2435. self._process_security_group_properties_update(
  2436. context, secgroup_res, sg_data)
  2437. domain_id = NSX_P_GLOBAL_DOMAIN_ID
  2438. # Update the name and description on NSX backend
  2439. if 'name' in sg_data or 'description' in sg_data:
  2440. nsx_name = utils.get_name_and_uuid(
  2441. secgroup_res['name'] or 'securitygroup', sg_id)
  2442. try:
  2443. self.nsxpolicy.group.update(
  2444. domain_id, sg_id,
  2445. name=nsx_name,
  2446. description=secgroup_res.get('description', ''))
  2447. self.nsxpolicy.comm_map.update(
  2448. domain_id, sg_id,
  2449. name=nsx_name,
  2450. description=secgroup_res.get('description', ''))
  2451. except Exception as e:
  2452. LOG.warning("Failed to update SG %s NSX resources: %s",
  2453. sg_id, e)
  2454. # Go on with the update anyway (it's just the name & desc)
  2455. # If the logging of the SG changed - update the backend rules
  2456. if sg_logging.LOGGING in sg_data:
  2457. logged = (sg_data[sg_logging.LOGGING] or
  2458. cfg.CONF.nsx_p.log_security_groups_allowed_traffic)
  2459. self.nsxpolicy.comm_map.update_entries_logged(domain_id, sg_id,
  2460. logged)
  2461. return secgroup_res
  2462. def delete_security_group(self, context, sg_id):
  2463. self._prevent_non_admin_edit_provider_sg(context, sg_id)
  2464. sg = self.get_security_group(context, sg_id)
  2465. super(NsxPolicyPlugin, self).delete_security_group(context, sg_id)
  2466. domain_id = NSX_P_GLOBAL_DOMAIN_ID
  2467. try:
  2468. self.nsxpolicy.comm_map.delete(domain_id, sg_id)
  2469. self.nsxpolicy.group.delete(domain_id, sg_id)
  2470. for rule in sg['security_group_rules']:
  2471. self._delete_security_group_rule_backend_resources(
  2472. context, rule)
  2473. except nsx_lib_exc.ResourceNotFound:
  2474. # If the resource was not found on the backend do not worry about
  2475. # it. The conditions has already been logged, so there is no need
  2476. # to do further logging
  2477. pass
  2478. except nsx_lib_exc.ManagerError as e:
  2479. # If there is a failure in deleting the resource, fail the neutron
  2480. # operation even though the neutron object was already deleted.
  2481. # This way the user will be aware of zombie resources that may fail
  2482. # future actions.
  2483. msg = (_("Backend security group objects deletion for neutron "
  2484. "security group %(id)s failed. The object was however "
  2485. "removed from the Neutron database: %(e)s") %
  2486. {'id': sg_id, 'e': e})
  2487. raise nsx_exc.NsxPluginException(err_msg=msg)
  2488. def create_security_group_rule(self, context, security_group_rule):
  2489. bulk_rule = {'security_group_rules': [security_group_rule]}
  2490. return self.create_security_group_rule_bulk(context, bulk_rule)[0]
  2491. def create_security_group_rule_bulk(self, context, security_group_rules):
  2492. sg_rules = security_group_rules['security_group_rules']
  2493. for r in sg_rules:
  2494. self._check_local_ip_prefix(context, r['security_group_rule'])
  2495. # Tenant & security group are the same for all rules in the bulk
  2496. example_rule = sg_rules[0]['security_group_rule']
  2497. sg_id = example_rule['security_group_id']
  2498. sg = self.get_security_group(context, sg_id)
  2499. self._prevent_non_admin_edit_provider_sg(context, sg_id)
  2500. with db_api.CONTEXT_WRITER.using(context):
  2501. rules_db = (super(NsxPolicyPlugin,
  2502. self).create_security_group_rule_bulk_native(
  2503. context, security_group_rules))
  2504. for i, r in enumerate(sg_rules):
  2505. self._process_security_group_rule_properties(
  2506. context, rules_db[i], r['security_group_rule'])
  2507. is_provider_sg = sg.get(provider_sg.PROVIDER)
  2508. secgroup_logging = self._is_security_group_logged(context, sg_id)
  2509. category = (NSX_P_PROVIDER_SECTION_CATEGORY if is_provider_sg
  2510. else NSX_P_REGULAR_SECTION_CATEGORY)
  2511. # Create the NSX backend rules in a single transaction
  2512. with policy_trans.NsxPolicyTransaction():
  2513. # Build new rules and relevant objects
  2514. backend_rules = []
  2515. for rule_data in rules_db:
  2516. rule_entry = self._create_security_group_backend_rule(
  2517. context, sg_id, rule_data, secgroup_logging,
  2518. is_provider_sg=is_provider_sg)
  2519. backend_rules.append(rule_entry)
  2520. # Add the old rules
  2521. for rule in sg['security_group_rules']:
  2522. rule_entry = self.nsxpolicy.comm_map.build_entry(
  2523. NSX_P_GLOBAL_DOMAIN_ID, sg_id, rule['id'])
  2524. backend_rules.append(rule_entry)
  2525. # Update the policy with all the rules.
  2526. self.nsxpolicy.comm_map.update_with_entries(
  2527. NSX_P_GLOBAL_DOMAIN_ID, sg_id, entries=backend_rules,
  2528. category=category)
  2529. return rules_db
  2530. def _delete_security_group_rule_backend_resources(
  2531. self, context, rule_db):
  2532. rule_id = rule_db['id']
  2533. # try to delete the service of this rule, if exists
  2534. if rule_db['protocol']:
  2535. try:
  2536. self.nsxpolicy.service.delete(rule_id)
  2537. except nsx_lib_exc.ResourceNotFound:
  2538. pass
  2539. # Try to delete the remote ip prefix group, if exists
  2540. if rule_db['remote_ip_prefix']:
  2541. try:
  2542. remote_group_id = self._get_sg_rule_remote_ip_group_id(rule_db)
  2543. self.nsxpolicy.group.delete(NSX_P_GLOBAL_DOMAIN_ID,
  2544. remote_group_id)
  2545. except nsx_lib_exc.ResourceNotFound:
  2546. pass
  2547. # Try to delete the local ip prefix group, if exists
  2548. if self._get_security_group_rule_local_ip(context, rule_id):
  2549. try:
  2550. local_group_id = self._get_sg_rule_local_ip_group_id(rule_db)
  2551. self.nsxpolicy.group.delete(NSX_P_GLOBAL_DOMAIN_ID,
  2552. local_group_id)
  2553. except nsx_lib_exc.ResourceNotFound:
  2554. pass
  2555. def delete_security_group_rule(self, context, rule_id):
  2556. rule_db = self._get_security_group_rule(context, rule_id)
  2557. sg_id = rule_db['security_group_id']
  2558. self._prevent_non_admin_edit_provider_sg(context, sg_id)
  2559. # Delete the rule itself
  2560. try:
  2561. self.nsxpolicy.comm_map.delete_entry(
  2562. policy_constants.DEFAULT_DOMAIN, sg_id, rule_id)
  2563. self._delete_security_group_rule_backend_resources(
  2564. context, rule_db)
  2565. except nsx_lib_exc.ResourceNotFound:
  2566. # Go on with the deletion anyway
  2567. pass
  2568. except nsx_lib_exc.ManagerError as e:
  2569. msg = (_("Backend security group rule deletion for neutron "
  2570. "rule %(id)s failed: %(e)s") % {'id': rule_id, 'e': e})
  2571. nsx_exc.NsxPluginException(err_msg=msg)
  2572. super(NsxPolicyPlugin, self).delete_security_group_rule(
  2573. context, rule_id)
  2574. def _is_overlay_network(self, context, network_id):
  2575. """Return True if this is an overlay network
  2576. 1. No binding ("normal" overlay networks will have no binding)
  2577. 2. Geneve network
  2578. 3. nsx network where the backend network is connected to an overlay TZ
  2579. """
  2580. bindings = nsx_db.get_network_bindings(context.session, network_id)
  2581. # With NSX plugin, "normal" overlay networks will have no binding
  2582. if not bindings:
  2583. # using the default/AZ overlay_tz
  2584. return True
  2585. binding = bindings[0]
  2586. if binding.binding_type == utils.NsxV3NetworkTypes.GENEVE:
  2587. return True
  2588. if binding.binding_type == utils.NsxV3NetworkTypes.NSX_NETWORK:
  2589. # check the backend network
  2590. segment = self.nsxpolicy.segment.get(binding.phy_uuid)
  2591. tz = self._get_nsx_net_tz_id(segment)
  2592. if tz:
  2593. # This call is cached on the nsxlib side
  2594. type = self.nsxpolicy.transport_zone.get_transport_type(
  2595. tz)
  2596. return type == nsxlib_consts.TRANSPORT_TYPE_OVERLAY
  2597. def _is_ens_tz(self, tz_id):
  2598. # This call is cached on the nsxlib side
  2599. mode = self.nsxpolicy.transport_zone.get_host_switch_mode(tz_id)
  2600. return mode == nsxlib_consts.HOST_SWITCH_MODE_ENS
  2601. def _has_native_dhcp_metadata(self):
  2602. return True
  2603. def _get_tier0_uplink_cidrs(self, tier0_id):
  2604. # return a list of tier0 uplink ip/prefix addresses
  2605. return self.nsxpolicy.tier0.get_uplink_cidrs(tier0_id)
  2606. def _is_vlan_router_interface_supported(self):
  2607. return True
  2608. def _get_neutron_net_ids_by_nsx_id(self, context, lswitch_id):
  2609. """Translate nsx ls IDs given by Nova to neutron network ids.
  2610. Since there is no DB mapping for this, the plugin will query the NSX
  2611. for this, and cache the results.
  2612. """
  2613. if lswitch_id not in NET_NSX_2_NEUTRON_ID_CACHE:
  2614. # Go to the nsx using passthrough api to get the neutron id
  2615. if not cfg.CONF.nsx_p.allow_passthrough:
  2616. LOG.warning("Cannot get neutron id for ls %s without "
  2617. "passthrough api", lswitch_id)
  2618. return []
  2619. ls = self.nsxlib.logical_switch.get(lswitch_id)
  2620. neutron_id = None
  2621. for tag in ls.get('tags', []):
  2622. if tag['scope'] == 'os-neutron-net-id':
  2623. neutron_id = tag['tag']
  2624. break
  2625. if neutron_id:
  2626. # Cache the result
  2627. NET_NSX_2_NEUTRON_ID_CACHE[lswitch_id] = neutron_id
  2628. NET_NEUTRON_2_NSX_ID_CACHE[neutron_id] = lswitch_id
  2629. if NET_NSX_2_NEUTRON_ID_CACHE.get(lswitch_id):
  2630. return [NET_NSX_2_NEUTRON_ID_CACHE[lswitch_id]]
  2631. return []
  2632. def _get_net_tz(self, context, net_id):
  2633. bindings = nsx_db.get_network_bindings(context.session, net_id)
  2634. if bindings:
  2635. bind_type = bindings[0].binding_type
  2636. if bind_type == utils.NsxV3NetworkTypes.NSX_NETWORK:
  2637. # If it is an NSX network, return the TZ of the backend segment
  2638. segment_id = bindings[0].phy_uuid
  2639. return self.nsxpolicy.segment.get_transport_zone_id(segment_id)
  2640. elif bind_type == utils.NetworkTypes.L3_EXT:
  2641. # External network has tier0 as phy_uuid
  2642. return
  2643. else:
  2644. return bindings[0].phy_uuid
  2645. else:
  2646. # Get the default one for the network AZ
  2647. az = self.get_network_az_by_net_id(context, net_id)
  2648. return az._default_overlay_tz_uuid
  2649. def _validate_router_tz(self, context, tier0_uuid, subnets):
  2650. # make sure the related GW (Tier0 router) belongs to the same TZ
  2651. # as the subnets attached to the Tier1 router
  2652. if not subnets or not tier0_uuid:
  2653. return
  2654. tier0_tzs = self.nsxpolicy.tier0.get_transport_zones(tier0_uuid)
  2655. if not tier0_tzs:
  2656. return
  2657. for sub in subnets:
  2658. tz_uuid = self._get_net_tz(context, sub['network_id'])
  2659. if tz_uuid not in tier0_tzs:
  2660. msg = (_("Tier0 router %(rtr)s transport zone should match "
  2661. "transport zone %(tz)s of the network %(net)s") % {
  2662. 'rtr': tier0_uuid,
  2663. 'tz': tz_uuid,
  2664. 'net': sub['network_id']})
  2665. raise n_exc.InvalidInput(error_message=msg)
  2666. def _get_net_dhcp_relay(self, context, net_id):
  2667. # No dhcp relay support yet
  2668. return None
  2669. def _support_vlan_router_interfaces(self):
  2670. return True
  2671. def update_router_firewall(self, context, router_id, router_db=None,
  2672. from_fw=False):
  2673. """Rewrite all the rules in the router edge firewall
  2674. This method should be called on FWaaS v2 updates, and on router
  2675. interfaces changes.
  2676. When FWaaS is disabled, there is no need to update the NSX router FW,
  2677. as the default rule is allow-all.
  2678. """
  2679. if (self.fwaas_callbacks and
  2680. self.fwaas_callbacks.fwaas_enabled):
  2681. if not router_db:
  2682. router_db = self._get_router(context, router_id)
  2683. # find all the relevant ports of the router for FWaaS v2
  2684. # TODO(asarfaty): Add vm ports as well
  2685. ports = self._get_router_interfaces(context, router_id)
  2686. # let the fwaas callbacks update the router FW
  2687. return self.fwaas_callbacks.update_router_firewall(
  2688. context, router_id, router_db, ports, called_from_fw=from_fw)
  2689. def update_port_nsx_tags(self, context, port_id, tags, is_delete=False):
  2690. """Update backend NSX segment port with tags from the tagging plugin"""
  2691. # Make sure it is a backend port
  2692. ctx = n_context.get_admin_context()
  2693. port_data = self.get_port(ctx, port_id)
  2694. if not self._is_backend_port(ctx, port_data):
  2695. LOG.info("Ignoring tags on port %s: this port has no backend "
  2696. "NSX logical port", port_id)
  2697. return
  2698. # Get the current tags on this port
  2699. segment_id = self._get_network_nsx_segment_id(
  2700. ctx, port_data['network_id'])
  2701. lport = self.nsxpolicy.segment_port.get(segment_id, port_id)
  2702. port_tags = lport.get('tags')
  2703. orig_len = len(port_tags)
  2704. # Update and validate the list of tags
  2705. extra_tags = self._translate_external_tags(tags, port_id)
  2706. if is_delete:
  2707. port_tags = [tag for tag in port_tags if tag not in extra_tags]
  2708. else:
  2709. port_tags.extend(
  2710. [tag for tag in extra_tags if tag not in port_tags])
  2711. if len(port_tags) > nsxlib_utils.MAX_TAGS:
  2712. LOG.warning("Cannot add external tags to port %s: "
  2713. "too many tags", port_id)
  2714. # Update the NSX port
  2715. if len(port_tags) != orig_len:
  2716. self.nsxpolicy.segment_port.update(
  2717. segment_id, port_id, tags=port_tags)
  2718. def get_extra_fw_rules(self, context, router_id, port_id):
  2719. """Return firewall rules that should be added to the router firewall
  2720. This method should return a list of allow firewall rules that are
  2721. required in order to enable different plugin features with north/south
  2722. traffic.
  2723. The returned rules will be added after the FWaaS rules, and before the
  2724. default drop rule.
  2725. Only rules relevant for port_id router interface port should be
  2726. returned, and the rules should be ingress/egress
  2727. (but not both) and include the source/dest nsx logical port.
  2728. """
  2729. extra_rules = []
  2730. # VPN rules:
  2731. vpn_plugin = directory.get_plugin(plugin_const.VPN)
  2732. if vpn_plugin:
  2733. vpn_driver = vpn_plugin.drivers[vpn_plugin.default_provider]
  2734. vpn_rules = (
  2735. vpn_driver._generate_ipsecvpn_firewall_rules(
  2736. self.plugin_type(), context, router_id=router_id))
  2737. if vpn_rules:
  2738. extra_rules.extend(vpn_rules)
  2739. return extra_rules
  2740. def _validate_net_mdproxy_tz(self, az, tz_uuid, mdproxy_uuid):
  2741. """Validate that the network TZ matches the mdproxy edge cluster"""
  2742. if not self.nsxlib:
  2743. # No passthrough api support
  2744. return True
  2745. if az.use_policy_md:
  2746. # Policy obj
  2747. md_ec_path = self.nsxpolicy.md_proxy.get(
  2748. mdproxy_uuid).get('edge_cluster_path')
  2749. md_ec = p_utils.path_to_id(md_ec_path)
  2750. else:
  2751. # MP obj
  2752. md_ec = self.nsxlib.native_md_proxy.get(
  2753. mdproxy_uuid).get('edge_cluster_id')
  2754. ec_nodes = self.nsxlib.edge_cluster.get_transport_nodes(md_ec)
  2755. ec_tzs = []
  2756. for tn_uuid in ec_nodes:
  2757. ec_tzs.extend(self.nsxlib.transport_node.get_transport_zones(
  2758. tn_uuid))
  2759. if tz_uuid not in ec_tzs:
  2760. return False
  2761. return True