def test_check_for_igmp_snooping_enabled(self): cfg.CONF.set_override('igmp_snooping_enable', False, group='OVS') net = self._create_network('net') ls = self.nb_api.db_find('Logical_Switch', ('name', '=', utils.ovn_name( net['id']))).execute(check_error=True)[0] self.assertEqual('false', ls['other_config'][ovn_const.MCAST_SNOOP]) self.assertEqual( 'false', ls['other_config'][ovn_const.MCAST_FLOOD_UNREGISTERED]) # Change the value of the configuration cfg.CONF.set_override('igmp_snooping_enable', True, group='OVS') # Call the maintenance task and check that the value has been # updated in the Logical Switch self.assertRaises(periodics.NeverAgain, self.maint.check_for_igmp_snoop_support) ls = self.nb_api.db_find('Logical_Switch', ('name', '=', utils.ovn_name( net['id']))).execute(check_error=True)[0] self.assertEqual('true', ls['other_config'][ovn_const.MCAST_SNOOP]) self.assertEqual( 'true', ls['other_config'][ovn_const.MCAST_FLOOD_UNREGISTERED])
def _get_update_data_without_compare(self): lswitch_ovsdb_dict = {} for switch_name in self.lswitch_names: switch_name = utils.ovn_name(switch_name) lswitch = idlutils.row_by_value(self.api.idl, 'Logical_Switch', 'name', switch_name) lswitch_ovsdb_dict[switch_name] = lswitch if self.is_add_acl: acl_add_values_dict = {} for port in self.port_list: switch_name = utils.ovn_name(port['network_id']) if switch_name not in acl_add_values_dict: acl_add_values_dict[switch_name] = [] if port['id'] in self.acl_new_values_dict: acl_add_values_dict[switch_name].append( self.acl_new_values_dict[port['id']]) acl_del_objs_dict = {} else: acl_add_values_dict = {} acl_del_objs_dict = {} del_acl_extids = [] for acl_dict in self.acl_new_values_dict.values(): del_acl_extids.append({acl_dict['match']: acl_dict['external_ids']}) for switch_name, lswitch in lswitch_ovsdb_dict.items(): if switch_name not in acl_del_objs_dict: acl_del_objs_dict[switch_name] = [] acls = getattr(lswitch, 'acls', []) for acl in acls: match = getattr(acl, 'match') acl_extids = {match: getattr(acl, 'external_ids')} if acl_extids in del_acl_extids: acl_del_objs_dict[switch_name].append(acl) return lswitch_ovsdb_dict, acl_del_objs_dict, acl_add_values_dict
def add_acl_dhcp(port, subnet, ovn_dhcp=True): # Allow DHCP requests for OVN native DHCP service, while responses are # allowed in ovn-northd. # Allow both DHCP requests and responses to pass for other DHCP services. # We do this even if DHCP isn't enabled for the subnet acl_list = [] if not ovn_dhcp: acl = {"lswitch": utils.ovn_name(port['network_id']), "lport": port['id'], "priority": ovn_const.ACL_PRIORITY_ALLOW, "action": ovn_const.ACL_ACTION_ALLOW, "log": False, "name": [], "severity": [], "direction": 'to-lport', "match": ('outport == "%s" && ip4 && ip4.src == %s && ' 'udp && udp.src == 67 && udp.dst == 68' ) % (port['id'], subnet['cidr']), "external_ids": {'neutron:lport': port['id']}} acl_list.append(acl) acl = {"lswitch": utils.ovn_name(port['network_id']), "lport": port['id'], "priority": ovn_const.ACL_PRIORITY_ALLOW, "action": ovn_const.ACL_ACTION_ALLOW, "log": False, "name": [], "severity": [], "direction": 'from-lport', "match": ('inport == "%s" && ip4 && ' 'ip4.dst == {255.255.255.255, %s} && ' 'udp && udp.src == 68 && udp.dst == 67' ) % (port['id'], subnet['cidr']), "external_ids": {'neutron:lport': port['id']}} acl_list.append(acl) return acl_list
def test_overlap_net_logs(self): log_data1 = self._log_data(sg_id=self.sg3, port_id=self.port3) log_obj1 = self.log_plugin.create_log(self.ctxt, log_data1) self._check_sgrs(sgrs=self.sg1rs, is_enabled=False) self._check_sgrs(sgrs=self.sg2rs, is_enabled=False) self._check_sgrs(sgrs=self.sg3rs, is_enabled=True) log_data2 = self._log_data(port_id=self.port2) log_obj2 = self.log_plugin.create_log(self.ctxt, log_data2) self._check_sgrs(sgrs=self.sg1rs, is_enabled=False) # port 2 uses sg2 and sg3. However, sg3 is in use by log_obj1 # so only acls for 2 would be associated with log_obj2 for sgr in self.sg2rs: acl = self._check_acl_log(sgr) self.assertEqual(utils.ovn_name(log_obj2['id']), acl.name[0]) for sgr in self.sg3rs: acl = self._check_acl_log(sgr) self.assertEqual(utils.ovn_name(log_obj1['id']), acl.name[0]) # Next, delete log_obj1 and make sure that lob_obj2 gets to # claim what it could not use before self.log_plugin.delete_log(self.ctxt, log_obj1['id']) self._check_sgrs(sgrs=self.sg1rs, is_enabled=False) for sgr in self.sg2rs + self.sg3rs: acl = self._check_acl_log(sgr) self.assertEqual(utils.ovn_name(log_obj2['id']), acl.name[0]) # Delete log_obj2 and ensure that logs are off and meter is no # longer used self.log_plugin.delete_log(self.ctxt, log_obj2['id']) self._check_sgrs(is_enabled=False) self.assertEqual([], self.nb_api.meter_list().execute(check_error=True))
def test_get_all_logical_switches_with_ports(self): # Test empty mapping = self.nb_ovn_idl.get_all_logical_switches_with_ports() self.assertItemsEqual(mapping, {}) # Test loaded values self._load_nb_db() mapping = self.nb_ovn_idl.get_all_logical_switches_with_ports() expected = [{ 'name': utils.ovn_name('ls-id-1'), 'ports': ['lsp-id-11', 'lsp-id-12', 'lsp-rp-id-1'], 'provnet_port': 'provnet-ls-id-1' }, { 'name': utils.ovn_name('ls-id-2'), 'ports': ['lsp-id-21', 'lsp-rp-id-2'], 'provnet_port': 'provnet-ls-id-2' }, { 'name': utils.ovn_name('ls-id-3'), 'ports': ['lsp-id-31', 'lsp-id-32', 'lsp-rp-id-3', 'lsp-vpn-id-3'], 'provnet_port': None }, { 'name': utils.ovn_name('ls-id-5'), 'ports': ['lsp-id-51', 'lsp-id-52', 'lsp-rp-id-5', 'lsp-vpn-id-5'], 'provnet_port': None }] self.assertItemsEqual(mapping, expected)
def get_acls_for_lswitches(self, lswitch_names): """Get the existing set of acls that belong to the logical switches @param lswitch_names: List of logical switch names @type lswitch_names: [] @var acl_values_dict: A dictionary indexed by port_id containing the list of acl values in string format that belong to that port @var acl_obj_dict: A dictionary indexed by acl value containing the corresponding acl idl object. @var lswitch_ovsdb_dict: A dictionary mapping from logical switch name to lswitch idl object @return: (acl_values_dict, acl_obj_dict, lswitch_ovsdb_dict) """ acl_values_dict = {} acl_obj_dict = {} lswitch_ovsdb_dict = {} for lswitch_name in lswitch_names: try: lswitch = idlutils.row_by_value(self.idl, 'Logical_Switch', 'name', utils.ovn_name(lswitch_name)) except idlutils.RowNotFound: # It is possible for the logical switch to be deleted # while we are searching for it by name in idl. continue lswitch_ovsdb_dict[lswitch_name] = lswitch acls = getattr(lswitch, 'acls', []) # Iterate over each acl in a lswitch and store the acl in # a key:value representation for e.g. acl_string. This # key:value representation can invoke the code - # self._ovn.add_acl(**acl_string) for acl in acls: ext_ids = getattr(acl, 'external_ids', {}) port_id = ext_ids.get('neutron:lport') acl_list = acl_values_dict.setdefault(port_id, []) acl_string = { 'lport': port_id, 'lswitch': utils.ovn_name(lswitch_name) } for acl_key in getattr(acl, "_data", {}): try: acl_string[acl_key] = getattr(acl, acl_key) except AttributeError: pass acl_obj_dict[str(acl_string)] = acl acl_list.append(acl_string) return acl_values_dict, acl_obj_dict, lswitch_ovsdb_dict
def from_neutron_router(router): def _get_subnet_id(gw_info): subnet_id = '' ext_ips = gw_info.get('external_fixed_ips', []) if ext_ips: subnet_id = ext_ips[0]['subnet_id'] return subnet_id external_ids = { ovn_const.OVN_GW_PORT_EXT_ID_KEY: router.get('gw_port_id') or '', ovn_const.OVN_ROUTER_NAME_EXT_ID_KEY: router.get('name', 'no_router_name')} # Get the routes routes = [] for r in router.get('routes', []): routes.append(FakeStaticRoute(ip_prefix=r['destination'], nexthop=r['nexthop'], external_ids={})) gw_info = router.get(l3.EXTERNAL_GW_INFO) if gw_info: external_ids = { ovn_const.OVN_ROUTER_IS_EXT_GW: 'true', ovn_const.OVN_SUBNET_EXT_ID_KEY: _get_subnet_id(gw_info)} routes.append(FakeStaticRoute( ip_prefix='0.0.0.0/0', nexthop='', external_ids=external_ids)) return FakeOVNRouter.create_one_router( {'external_ids': external_ids, 'enabled': router.get('admin_state_up') or False, 'name': ovn_utils.ovn_name(router['id']), 'static_routes': routes})
def _test_external_port_update(self, vnic_type): net_id = self.n1['network']['id'] port_data = { 'port': { 'network_id': net_id, 'tenant_id': self._tenant_id } } port_req = self.new_create_request('ports', port_data, self.fmt) port_res = port_req.get_response(self.api) port = self.deserialize(self.fmt, port_res)['port'] ovn_port = self._find_port_row_by_name(port['id']) self.assertEqual('', ovn_port.type) self.assertEqual([], ovn_port.ha_chassis_group) port_upt_data = {'port': {portbindings.VNIC_TYPE: vnic_type}} port_req = self.new_update_request('ports', port_upt_data, port['id'], self.fmt) port_res = port_req.get_response(self.api) port = self.deserialize(self.fmt, port_res)['port'] ovn_port = self._find_port_row_by_name(port['id']) self.assertEqual(ovn_const.LSP_TYPE_EXTERNAL, ovn_port.type) self.assertEqual(1, len(ovn_port.ha_chassis_group)) self.assertEqual(utils.ovn_name(net_id), str(ovn_port.ha_chassis_group[0].name))
def _find_pf_lb(self, router_id, fip_id=None): lbs = self.nb_api.get_router_floatingip_lbs(utils.ovn_name(router_id)) return [ lb for lb in lbs if (not fip_id or fip_id == lb.external_ids[ovn_const.OVN_FIP_EXT_ID_KEY]) ]
def test_get_all_chassis_gateway_bindings(self): self._load_nb_db() bindings = self.nb_ovn_idl.get_all_chassis_gateway_bindings() expected = { 'host-1': [ utils.ovn_lrouter_port_name('orp-id-a1'), utils.ovn_lrouter_port_name('orp-id-a2') ], 'host-2': [utils.ovn_lrouter_port_name('orp-id-b2')], ovn_const.OVN_GATEWAY_INVALID_CHASSIS: [utils.ovn_name('orp-id-a3')] } self.assertItemsEqual(bindings, expected) bindings = self.nb_ovn_idl.get_all_chassis_gateway_bindings([]) self.assertItemsEqual(bindings, expected) bindings = self.nb_ovn_idl.get_all_chassis_gateway_bindings(['host-1']) expected = { 'host-1': [ utils.ovn_lrouter_port_name('orp-id-a1'), utils.ovn_lrouter_port_name('orp-id-a2') ] } self.assertItemsEqual(bindings, expected)
def update_floatingip(self, txn, floatingip): router_id = floatingip.get('router_id') qos_policy_id = floatingip.get('qos_policy_id') if floatingip['floating_network_id']: lswitch_name = utils.ovn_name(floatingip['floating_network_id']) txn.add( self._driver._nb_idl.qos_del_ext_ids( lswitch_name, {ovn_const.OVN_FIP_EXT_ID_KEY: floatingip['id']})) if not (router_id and qos_policy_id): return admin_context = n_context.get_admin_context() router_db = self._plugin_l3._get_router(admin_context, router_id) gw_port_id = router_db.get('gw_port_id') if not gw_port_id: return qos_rules = self._qos_rules(admin_context, qos_policy_id) for direction, rules in qos_rules.items(): ovn_rule = self._ovn_qos_rule( direction, rules, gw_port_id, floatingip['floating_network_id'], fip_id=floatingip['id'], ip_address=floatingip['floating_ip_address']) if ovn_rule: txn.add(self._driver._nb_idl.qos_add(**ovn_rule))
def _check_rules(self, rules, port_id, network_id): egress_ovn_rule = self.qos_driver._ovn_qos_rule( constants.EGRESS_DIRECTION, rules.get(constants.EGRESS_DIRECTION), port_id, network_id) ingress_ovn_rule = self.qos_driver._ovn_qos_rule( constants.INGRESS_DIRECTION, rules.get(constants.INGRESS_DIRECTION), port_id, network_id) with self.nb_api.transaction(check_error=True): ls = self.qos_driver._driver._nb_idl.lookup( 'Logical_Switch', ovn_utils.ovn_name(self.network_1)) self.assertEqual(len(rules), len(ls.qos_rules)) for rule in ls.qos_rules: ref_rule = (egress_ovn_rule if rule.direction == 'from-lport' else ingress_ovn_rule) action = {} if 'dscp' in ref_rule: action = {'dscp': ref_rule['dscp']} bandwidth = {} if 'rate' in ref_rule: bandwidth['rate'] = ref_rule['rate'] if ref_rule.get('burst'): bandwidth['burst'] = ref_rule['burst'] self.assertIn(port_id, rule.match) self.assertEqual(action, rule.action) self.assertEqual(bandwidth, rule.bandwidth)
def _delete_gateway_ip_qos_rules(self, txn, router_id, network_id): if network_id: lswitch_name = utils.ovn_name(network_id) txn.add( self.nb_idl.qos_del_ext_ids( lswitch_name, {ovn_const.OVN_ROUTER_ID_EXT_ID_KEY: router_id}))
def get_lrouter(self, lrouter_name): if uuidutils.is_uuid_like(lrouter_name): lrouter_name = utils.ovn_name(lrouter_name) try: return self.lr_get(lrouter_name).execute(check_error=True) except idlutils.RowNotFound: return None
def _ovn_qos_rule(self, rules_direction, rules, port_id, network_id, delete=False): """Generate an OVN QoS register based on several Neutron QoS rules A OVN QoS register can contain "bandwidth" and "action" parameters. "bandwidth" defines the rate speed limitation; "action" contains the DSCP value to apply. Both are not exclusive. Only one rule per port and direction can be applied; that's why two rules (bandwidth limit and DSCP) in the same direction must be combined in one OVN QoS register. http://www.openvswitch.org/support/dist-docs/ovn-nb.5.html :param rules_direction: (string) rules direction (egress, ingress). :param rules: (dict) {bw_limit: {max_kbps, max_burst_kbps}, dscp: {dscp_mark}} :param port_id: (string) port ID. :param network_id: (string) network ID. :param delete: (bool) defines if this rule if going to be a partial one (without any bandwidth or DSCP information) to be used only as deletion rule. :return: (dict) OVN QoS rule register to be used with QoSAddCommand and QoSDelCommand. """ if not delete and not rules: return lswitch_name = utils.ovn_name(network_id) if rules_direction == constants.EGRESS_DIRECTION: direction = 'from-lport' match = 'inport == "{}"'.format(port_id) else: direction = 'to-lport' match = 'outport == "{}"'.format(port_id) ovn_qos_rule = { 'switch': lswitch_name, 'direction': direction, 'priority': OVN_QOS_DEFAULT_RULE_PRIORITY, 'match': match } if delete: # Any specific rule parameter is left undefined. return ovn_qos_rule for rule_type, rule in rules.items(): if rule_type == qos_consts.RULE_TYPE_BANDWIDTH_LIMIT: ovn_qos_rule['rate'] = rule['max_kbps'] if rule.get('max_burst_kbps'): ovn_qos_rule['burst'] = rule['max_burst_kbps'] elif rule_type == qos_consts.RULE_TYPE_DSCP_MARKING: ovn_qos_rule.update({'dscp': rule['dscp_mark']}) return ovn_qos_rule
def _find_nat_rule(self, router_id, external_ip, logical_ip=None, nat_type='dnat_and_snat'): rules = self.nb_api.get_lrouter_nat_rules(utils.ovn_name(router_id)) return next((r for r in rules if r['type'] == nat_type and r['external_ip'] == external_ip and (not logical_ip or r['logical_ip'] == logical_ip)), None)
def get_lrouter(self, lrouter_name): if uuidutils.is_uuid_like(lrouter_name): lrouter_name = utils.ovn_name(lrouter_name) # TODO(lucasagomes): Use lr_get() once we start refactoring this # API to use methods from ovsdbapp. lr = self.db_find_rows('Logical_Router', ('name', '=', lrouter_name)) result = lr.execute(check_error=True) return result[0] if result else None
def get_floatingip_by_ips(self, router_id, logical_ip, external_ip): if not all([router_id, logical_ip, external_ip]): return for nat in self.get_lrouter_nat_rules(utils.ovn_name(router_id)): if (nat['type'] == 'dnat_and_snat' and nat['logical_ip'] == logical_ip and nat['external_ip'] == external_ip): return nat
def _get_lbs_and_ls(self, nb_ovn, payload): rtr_name = ovn_utils.ovn_name(payload.resource_id) ovn_lr = nb_ovn.get_lrouter(rtr_name) if ovn_lr: ext_id_key = ovn_const.OVN_DEVICE_OWNER_EXT_ID_KEY # Filter only lbs managed by port forwarding plugin lr_lbs = [ lr for lr in ovn_lr.load_balancer if lr.external_ids.get( ext_id_key) == pf_const.PORT_FORWARDING_PLUGIN ] r_port = payload.metadata.get('port') if r_port: ls_name = ovn_utils.ovn_name(r_port['network_id']) ovn_ls = nb_ovn.get_lswitch(ls_name) ls_lbs = ovn_ls.load_balancer return lr_lbs, ls_lbs, ls_name return [], [], None
def test_add_and_remove(self): self._check_sgrs(is_enabled=False) self.assertEqual([], self.nb_api.meter_list().execute(check_error=True)) log_obj = self.log_plugin.create_log(self.ctxt, self._log_data()) for sgr in self.sgrs: acl = self._check_acl_log(sgr) self.assertEqual(utils.ovn_name(log_obj['id']), acl.name[0]) meter = self.nb_api.meter_get( acl.meter[0]).execute(check_error=True) self.assertEqual([True], meter.fair) self.assertEqual('pktps', meter.unit) self.assertEqual(1, len(meter.bands)) self.assertEqual( { ovn_const.OVN_DEVICE_OWNER_EXT_ID_KEY: log_const.LOGGING_PLUGIN }, meter.external_ids) self.log_plugin.delete_log(self.ctxt, log_obj['id']) self._check_sgrs(is_enabled=False) self.assertEqual([], self.nb_api.meter_list().execute(check_error=True)) log_objs = [] for sg in self.sgs: log_data = self._log_data(sg_id=sg) log_objs.append(self.log_plugin.create_log(self.ctxt, log_data)) self.assertEqual(len(log_objs), len(self.log_plugin.get_logs(self.ctxt))) self._check_sgrs(is_enabled=True) # Attempt to delete non-existing row self.assertRaises(log_exc.LogResourceNotFound, self.log_plugin.delete_log, self.ctxt, log_obj['id']) self.log_plugin.delete_log(self.ctxt, log_objs[1]['id']) self._check_sgrs(sgrs=self.sg1rs, is_enabled=True) self._check_sgrs(sgrs=self.sg2rs, is_enabled=False) self._check_sgrs(sgrs=self.sg3rs, is_enabled=True) self.log_plugin.delete_log(self.ctxt, log_objs[2]['id']) self._check_sgrs(sgrs=self.sg1rs, is_enabled=True) self._check_sgrs(sgrs=self.sg2rs, is_enabled=False) self._check_sgrs(sgrs=self.sg3rs, is_enabled=False) self.log_plugin.delete_log(self.ctxt, log_objs[0]['id']) self.assertEqual([], self.log_plugin.get_logs(self.ctxt)) self._check_sgrs(is_enabled=False) # Attempt to delete from empty table self.assertRaises(log_exc.LogResourceNotFound, self.log_plugin.delete_log, self.ctxt, log_objs[0]['id'])
def get_lswitch(self, lswitch_name): # FIXME(lucasagomes): We should refactor those get_*() # methods. Some of 'em require the name, others IDs etc... It can # be confusing. if uuidutils.is_uuid_like(lswitch_name): lswitch_name = utils.ovn_name(lswitch_name) try: return self.lookup('Logical_Switch', lswitch_name) except idlutils.RowNotFound: return None
def update_log(self, context, log_obj): """Update a log_obj invocation. :param context: current running context information :param log_obj: a log object being updated """ LOG.debug("Update_log %s", log_obj) pgs = self._pgs_from_log_obj(context, log_obj) actions_enabled = self._acl_actions_enabled(log_obj) with self.ovn_nb.transaction(check_error=True) as ovn_txn: self._set_acls_log(pgs, ovn_txn, actions_enabled, utils.ovn_name(log_obj.id))
def add_sg_rule_acl_for_port(port, r, match): dir_map = {const.INGRESS_DIRECTION: 'to-lport', const.EGRESS_DIRECTION: 'from-lport'} acl = {"lswitch": utils.ovn_name(port['network_id']), "lport": port['id'], "priority": ovn_const.ACL_PRIORITY_ALLOW, "action": ovn_const.ACL_ACTION_ALLOW_RELATED, "log": False, "name": [], "severity": [], "direction": dir_map[r['direction']], "match": match, "external_ids": {'neutron:lport': port['id'], ovn_const.OVN_SG_RULE_EXT_ID_KEY: r['id']}} return acl
def get_gateway_chassis_az_hints(self, gateway_name): lrp = self.lookup('Logical_Router_Port', gateway_name, default=None) if not lrp: return [] router_id = lrp.external_ids.get(ovn_const.OVN_ROUTER_NAME_EXT_ID_KEY, "") lrouter = self.lookup('Logical_Router', utils.ovn_name(router_id), default=None) if not lrouter: return [] az_string = lrouter.external_ids.get(ovn_const.OVN_AZ_HINTS_EXT_ID_KEY, "") if not az_string: return [] return az_string.split(",")
def from_neutron_port(port): """Create a fake ovn port based on a neutron port.""" external_ids = { ovn_const.OVN_NETWORK_NAME_EXT_ID_KEY: ovn_utils.ovn_name(port['network_id']), ovn_const.OVN_SG_IDS_EXT_ID_KEY: ' '.join(port['security_groups']), ovn_const.OVN_DEVICE_OWNER_EXT_ID_KEY: port.get('device_owner', '')} addresses = [port['mac_address'], ] addresses += [x['ip_address'] for x in port.get('fixed_ips', [])] port_security = ( addresses + [x['ip_address'] for x in port.get('allowed_address_pairs', [])]) return FakeOVNPort.create_one_port( {'external_ids': external_ids, 'addresses': addresses, 'port_security': port_security})
def drop_all_ip_traffic_for_port(port): acl_list = [] for direction, p in (('from-lport', 'inport'), ('to-lport', 'outport')): lswitch = utils.ovn_name(port['network_id']) lport = port['id'] acl = {"lswitch": lswitch, "lport": lport, "priority": ovn_const.ACL_PRIORITY_DROP, "action": ovn_const.ACL_ACTION_DROP, "log": False, "name": [], "severity": [], "direction": direction, "match": '%s == "%s" && ip' % (p, port['id']), "external_ids": {'neutron:lport': port['id']}} acl_list.append(acl) return acl_list
def _test_external_port_update_switchdev(self, vnic_type): net_id = self.n1['network']['id'] port_data = { 'port': { 'network_id': net_id, 'tenant_id': self._tenant_id, portbindings.VNIC_TYPE: vnic_type } } # Create a VNIC_DIRECT[_PHYSICAL] type port without the "switchdev" # capability and assert that it's an "external" port port_req = self.new_create_request('ports', port_data, self.fmt) port_res = port_req.get_response(self.api) port = self.deserialize(self.fmt, port_res)['port'] ovn_port = self._find_port_row_by_name(port['id']) self.assertEqual(ovn_const.LSP_TYPE_EXTERNAL, ovn_port.type) self.assertEqual(1, len(ovn_port.ha_chassis_group)) self.assertEqual(utils.ovn_name(net_id), str(ovn_port.ha_chassis_group[0].name)) # Now, update the port to add a "switchdev" capability and make # sure it's not treated as an "external" port anymore nor it's # included in a HA Chassis Group port_upt_data = { 'port': { ovn_const.OVN_PORT_BINDING_PROFILE: { 'capabilities': [ovn_const.PORT_CAP_SWITCHDEV] } } } port_req = self.new_update_request('ports', port_upt_data, port['id'], self.fmt) port_res = port_req.get_response(self.api) port = self.deserialize(self.fmt, port_res)['port'] ovn_port = self._find_port_row_by_name(port['id']) # When "switchdev" is set, we should treat it as a normal # port instead of "external" type self.assertEqual("", ovn_port.type) # Assert the poer hasn't been added to any HA Chassis Group either self.assertEqual(0, len(ovn_port.ha_chassis_group))
def update_floatingip(self, txn, floatingip): router_id = floatingip.get('router_id') qos_policy_id = floatingip.get('qos_policy_id') if floatingip['floating_network_id']: lswitch_name = utils.ovn_name(floatingip['floating_network_id']) txn.add( self._driver._nb_idl.qos_del_ext_ids( lswitch_name, {ovn_const.OVN_FIP_EXT_ID_KEY: floatingip['id']})) if not (router_id and qos_policy_id): return admin_context = n_context.get_admin_context() router_db = self._plugin_l3._get_router(admin_context, router_id) gw_port_id = router_db.get('gw_port_id') if not gw_port_id: return if ovn_conf.is_ovn_distributed_floating_ip(): # DVR, floating IP GW is in the same compute node as private port. resident_port = floatingip['port_id'] else: # Non-DVR, floating IP GW is located where chassisredirect lrp is. resident_port = utils.ovn_cr_lrouter_port_name(gw_port_id) qos_rules = self._qos_rules(admin_context, qos_policy_id) for direction, rules in qos_rules.items(): ovn_rule = self._ovn_qos_rule( direction, rules, gw_port_id, floatingip['floating_network_id'], fip_id=floatingip['id'], ip_address=floatingip['floating_ip_address'], resident_port=resident_port) if ovn_rule: txn.add(self._driver._nb_idl.qos_add(**ovn_rule))
def delete_log(self, context, log_obj): """Delete a log_obj invocation. :param context: current running context information :param log_obj: a log_object being deleted """ LOG.debug("Delete_log %s", log_obj) # If we are removing the last log_obj, let's clear log from all acls. # This is a simple way of ensuring that no acl logs are left behind! log_objs = self._get_logs(context) if not log_objs or (len(log_objs) == 1 and log_objs[0].id == log_obj.id): pgs = self._pgs_all() with self.ovn_nb.transaction(check_error=True) as ovn_txn: self._remove_acls_log(pgs, ovn_txn) ovn_txn.add( self.ovn_nb.meter_del(self.meter_name, if_exists=True)) LOG.info("All ACL logs cleared after deletion of log_obj %s", log_obj.id) return # Remove log_obj and revisit all remaining ones, since the acls that # were serving the removed log_obj may be usable by the remaining # log_objs. pgs = self._pgs_from_log_obj(context, log_obj) with self.ovn_nb.transaction(check_error=True) as ovn_txn: self._remove_acls_log(pgs, ovn_txn, utils.ovn_name(log_obj.id)) # TODO(flaviof): We needed to break this second part into a separate # transaction because logic that determines the value of the 'freed up' # acl rows will not see the modified rows unless it was inside an an # idl command. with self.ovn_nb.transaction(check_error=True) as ovn_txn: self._update_log_objs( context, ovn_txn, [lo for lo in log_objs if lo.id != log_obj.id])
class TestNBImplIdlOvn(TestDBImplIdlOvn): fake_set = { 'lswitches': [{ 'name': utils.ovn_name('ls-id-1'), 'external_ids': { ovn_const.OVN_NETWORK_NAME_EXT_ID_KEY: 'ls-name-1' } }, { 'name': utils.ovn_name('ls-id-2'), 'external_ids': { ovn_const.OVN_NETWORK_NAME_EXT_ID_KEY: 'ls-name-2' } }, { 'name': utils.ovn_name('ls-id-3'), 'external_ids': { ovn_const.OVN_NETWORK_NAME_EXT_ID_KEY: 'ls-name-3' } }, { 'name': 'ls-id-4', 'external_ids': { 'not-neutron:network_name': 'ls-name-4' } }, { 'name': utils.ovn_name('ls-id-5'), 'external_ids': { ovn_const.OVN_NETWORK_NAME_EXT_ID_KEY: 'ls-name-5' } }], 'lswitch_ports': [{ 'name': 'lsp-id-11', 'addresses': ['10.0.1.1'], 'external_ids': { ovn_const.OVN_PORT_NAME_EXT_ID_KEY: 'lsp-name-11' } }, { 'name': 'lsp-id-12', 'addresses': ['10.0.1.2'], 'external_ids': { ovn_const.OVN_PORT_NAME_EXT_ID_KEY: 'lsp-name-12' } }, { 'name': 'lsp-rp-id-1', 'addresses': ['10.0.1.254'], 'external_ids': { ovn_const.OVN_PORT_NAME_EXT_ID_KEY: 'lsp-rp-name-1' }, 'options': { 'router-port': utils.ovn_lrouter_port_name('orp-id-a1') } }, { 'name': 'provnet-ls-id-1', 'addresses': ['unknown'], 'external_ids': {}, 'options': { 'network_name': 'physnet1' } }, { 'name': 'lsp-id-21', 'addresses': ['10.0.2.1'], 'external_ids': { ovn_const.OVN_PORT_NAME_EXT_ID_KEY: 'lsp-name-21' } }, { 'name': 'lsp-id-22', 'addresses': ['10.0.2.2'], 'external_ids': {} }, { 'name': 'lsp-id-23', 'addresses': ['10.0.2.3'], 'external_ids': { 'not-neutron:port_name': 'lsp-name-23' } }, { 'name': 'lsp-rp-id-2', 'addresses': ['10.0.2.254'], 'external_ids': { ovn_const.OVN_PORT_NAME_EXT_ID_KEY: 'lsp-rp-name-2' }, 'options': { 'router-port': utils.ovn_lrouter_port_name('orp-id-a2') } }, { 'name': 'provnet-ls-id-2', 'addresses': ['unknown'], 'external_ids': {}, 'options': { 'network_name': 'physnet2' } }, { 'name': 'lsp-id-31', 'addresses': ['10.0.3.1'], 'external_ids': { ovn_const.OVN_PORT_NAME_EXT_ID_KEY: 'lsp-name-31' } }, { 'name': 'lsp-id-32', 'addresses': ['10.0.3.2'], 'external_ids': { ovn_const.OVN_PORT_NAME_EXT_ID_KEY: 'lsp-name-32' } }, { 'name': 'lsp-rp-id-3', 'addresses': ['10.0.3.254'], 'external_ids': { ovn_const.OVN_PORT_NAME_EXT_ID_KEY: 'lsp-rp-name-3' }, 'options': { 'router-port': utils.ovn_lrouter_port_name('orp-id-a3') } }, { 'name': 'lsp-vpn-id-3', 'addresses': ['10.0.3.253'], 'external_ids': { ovn_const.OVN_PORT_NAME_EXT_ID_KEY: 'lsp-vpn-name-3' } }, { 'name': 'lsp-id-41', 'addresses': ['20.0.1.1'], 'external_ids': { 'not-neutron:port_name': 'lsp-name-41' } }, { 'name': 'lsp-rp-id-4', 'addresses': ['20.0.1.254'], 'external_ids': {}, 'options': { 'router-port': 'xrp-id-b1' } }, { 'name': 'lsp-id-51', 'addresses': ['20.0.2.1'], 'external_ids': { ovn_const.OVN_PORT_NAME_EXT_ID_KEY: 'lsp-name-51' } }, { 'name': 'lsp-id-52', 'addresses': ['20.0.2.2'], 'external_ids': { ovn_const.OVN_PORT_NAME_EXT_ID_KEY: 'lsp-name-52' } }, { 'name': 'lsp-rp-id-5', 'addresses': ['20.0.2.254'], 'external_ids': { ovn_const.OVN_PORT_NAME_EXT_ID_KEY: 'lsp-rp-name-5' }, 'options': { 'router-port': utils.ovn_lrouter_port_name('orp-id-b2') } }, { 'name': 'lsp-vpn-id-5', 'addresses': ['20.0.2.253'], 'external_ids': { ovn_const.OVN_PORT_NAME_EXT_ID_KEY: 'lsp-vpn-name-5' } }], 'lrouters': [{ 'name': utils.ovn_name('lr-id-a'), 'external_ids': { ovn_const.OVN_ROUTER_NAME_EXT_ID_KEY: 'lr-name-a' } }, { 'name': utils.ovn_name('lr-id-b'), 'external_ids': { ovn_const.OVN_ROUTER_NAME_EXT_ID_KEY: 'lr-name-b' } }, { 'name': utils.ovn_name('lr-id-c'), 'external_ids': { ovn_const.OVN_ROUTER_NAME_EXT_ID_KEY: 'lr-name-c' } }, { 'name': utils.ovn_name('lr-id-d'), 'external_ids': { ovn_const.OVN_ROUTER_NAME_EXT_ID_KEY: 'lr-name-d' } }, { 'name': utils.ovn_name('lr-id-e'), 'external_ids': { ovn_const.OVN_ROUTER_NAME_EXT_ID_KEY: 'lr-name-e' } }], 'lrouter_ports': [{ 'name': utils.ovn_lrouter_port_name('orp-id-a1'), 'external_ids': {}, 'networks': ['10.0.1.0/24'], 'options': { ovn_const.OVN_GATEWAY_CHASSIS_KEY: 'host-1' } }, { 'name': utils.ovn_lrouter_port_name('orp-id-a2'), 'external_ids': {}, 'networks': ['10.0.2.0/24'], 'options': { ovn_const.OVN_GATEWAY_CHASSIS_KEY: 'host-1' } }, { 'name': utils.ovn_lrouter_port_name('orp-id-a3'), 'external_ids': {}, 'networks': ['10.0.3.0/24'], 'options': { ovn_const.OVN_GATEWAY_CHASSIS_KEY: ovn_const.OVN_GATEWAY_INVALID_CHASSIS } }, { 'name': 'xrp-id-b1', 'external_ids': {}, 'networks': ['20.0.1.0/24'] }, { 'name': utils.ovn_lrouter_port_name('orp-id-b2'), 'external_ids': {}, 'networks': ['20.0.2.0/24'], 'options': { ovn_const.OVN_GATEWAY_CHASSIS_KEY: 'host-2' } }, { 'name': utils.ovn_lrouter_port_name('orp-id-b3'), 'external_ids': {}, 'networks': ['20.0.3.0/24'], 'options': {} }], 'static_routes': [{ 'ip_prefix': '20.0.0.0/16', 'nexthop': '10.0.3.253' }, { 'ip_prefix': '10.0.0.0/16', 'nexthop': '20.0.2.253' }], 'nats': [{ 'external_ip': '10.0.3.1', 'logical_ip': '20.0.0.0/16', 'type': 'snat' }, { 'external_ip': '20.0.2.1', 'logical_ip': '10.0.0.0/24', 'type': 'snat' }, { 'external_ip': '20.0.2.4', 'logical_ip': '10.0.0.4', 'type': 'dnat_and_snat', 'external_mac': [], 'logical_port': [] }, { 'external_ip': '20.0.2.5', 'logical_ip': '10.0.0.5', 'type': 'dnat_and_snat', 'external_mac': ['00:01:02:03:04:05'], 'logical_port': ['lsp-id-001'] }], 'acls': [{ 'unit_test_id': 1, 'action': 'allow-related', 'direction': 'from-lport', 'external_ids': { 'neutron:lport': 'lsp-id-11' }, 'match': 'inport == "lsp-id-11" && ip4' }, { 'unit_test_id': 2, 'action': 'allow-related', 'direction': 'to-lport', 'external_ids': { 'neutron:lport': 'lsp-id-11' }, 'match': 'outport == "lsp-id-11" && ip4.src == $as_ip4_id_1' }, { 'unit_test_id': 3, 'action': 'allow-related', 'direction': 'from-lport', 'external_ids': { 'neutron:lport': 'lsp-id-12' }, 'match': 'inport == "lsp-id-12" && ip4' }, { 'unit_test_id': 4, 'action': 'allow-related', 'direction': 'to-lport', 'external_ids': { 'neutron:lport': 'lsp-id-12' }, 'match': 'outport == "lsp-id-12" && ip4.src == $as_ip4_id_1' }, { 'unit_test_id': 5, 'action': 'allow-related', 'direction': 'from-lport', 'external_ids': { 'neutron:lport': 'lsp-id-21' }, 'match': 'inport == "lsp-id-21" && ip4' }, { 'unit_test_id': 6, 'action': 'allow-related', 'direction': 'to-lport', 'external_ids': { 'neutron:lport': 'lsp-id-21' }, 'match': 'outport == "lsp-id-21" && ip4.src == $as_ip4_id_2' }, { 'unit_test_id': 7, 'action': 'allow-related', 'direction': 'from-lport', 'external_ids': { 'neutron:lport': 'lsp-id-41' }, 'match': 'inport == "lsp-id-41" && ip4' }, { 'unit_test_id': 8, 'action': 'allow-related', 'direction': 'to-lport', 'external_ids': { 'neutron:lport': 'lsp-id-41' }, 'match': 'outport == "lsp-id-41" && ip4.src == $as_ip4_id_4' }, { 'unit_test_id': 9, 'action': 'allow-related', 'direction': 'from-lport', 'external_ids': { 'neutron:lport': 'lsp-id-52' }, 'match': 'inport == "lsp-id-52" && ip4' }, { 'unit_test_id': 10, 'action': 'allow-related', 'direction': 'to-lport', 'external_ids': { 'neutron:lport': 'lsp-id-52' }, 'match': 'outport == "lsp-id-52" && ip4.src == $as_ip4_id_5' }], 'dhcp_options': [{ 'cidr': '10.0.1.0/24', 'external_ids': { 'subnet_id': 'subnet-id-10-0-1-0' }, 'options': { 'mtu': '1442', 'router': '10.0.1.254' } }, { 'cidr': '10.0.2.0/24', 'external_ids': { 'subnet_id': 'subnet-id-10-0-2-0' }, 'options': { 'mtu': '1442', 'router': '10.0.2.254' } }, { 'cidr': '10.0.1.0/26', 'external_ids': { 'subnet_id': 'subnet-id-10-0-1-0', 'port_id': 'lsp-vpn-id-3' }, 'options': { 'mtu': '1442', 'router': '10.0.1.1' } }, { 'cidr': '20.0.1.0/24', 'external_ids': { 'subnet_id': 'subnet-id-20-0-1-0' }, 'options': { 'mtu': '1442', 'router': '20.0.1.254' } }, { 'cidr': '20.0.2.0/24', 'external_ids': { 'subnet_id': 'subnet-id-20-0-2-0', 'port_id': 'lsp-vpn-id-5' }, 'options': { 'mtu': '1442', 'router': '20.0.2.254' } }, { 'cidr': '2001:dba::/64', 'external_ids': { 'subnet_id': 'subnet-id-2001-dba', 'port_id': 'lsp-vpn-id-5' }, 'options': { 'server_id': '12:34:56:78:9a:bc' } }, { 'cidr': '30.0.1.0/24', 'external_ids': { 'port_id': 'port-id-30-0-1-0' }, 'options': { 'mtu': '1442', 'router': '30.0.2.254' } }, { 'cidr': '30.0.2.0/24', 'external_ids': {}, 'options': {} }], 'address_sets': [{ 'name': '$as_ip4_id_1', 'addresses': ['10.0.1.1', '10.0.1.2'], 'external_ids': { ovn_const.OVN_SG_EXT_ID_KEY: 'id_1' } }, { 'name': '$as_ip4_id_2', 'addresses': ['10.0.2.1'], 'external_ids': { ovn_const.OVN_SG_EXT_ID_KEY: 'id_2' } }, { 'name': '$as_ip4_id_3', 'addresses': ['10.0.3.1', '10.0.3.2'], 'external_ids': { ovn_const.OVN_SG_EXT_ID_KEY: 'id_3' } }, { 'name': '$as_ip4_id_4', 'addresses': ['20.0.1.1', '20.0.1.2'], 'external_ids': {} }, { 'name': '$as_ip4_id_5', 'addresses': ['20.0.2.1', '20.0.2.2'], 'external_ids': { ovn_const.OVN_SG_EXT_ID_KEY: 'id_5' } }], } fake_associations = { 'lstolsp': { utils.ovn_name('ls-id-1'): ['lsp-id-11', 'lsp-id-12', 'lsp-rp-id-1', 'provnet-ls-id-1'], utils.ovn_name('ls-id-2'): [ 'lsp-id-21', 'lsp-id-22', 'lsp-id-23', 'lsp-rp-id-2', 'provnet-ls-id-2' ], utils.ovn_name('ls-id-3'): ['lsp-id-31', 'lsp-id-32', 'lsp-rp-id-3', 'lsp-vpn-id-3'], 'ls-id-4': ['lsp-id-41', 'lsp-rp-id-4'], utils.ovn_name('ls-id-5'): ['lsp-id-51', 'lsp-id-52', 'lsp-rp-id-5', 'lsp-vpn-id-5'] }, 'lrtolrp': { utils.ovn_name('lr-id-a'): [ utils.ovn_lrouter_port_name('orp-id-a1'), utils.ovn_lrouter_port_name('orp-id-a2'), utils.ovn_lrouter_port_name('orp-id-a3') ], utils.ovn_name('lr-id-b'): ['xrp-id-b1', utils.ovn_lrouter_port_name('orp-id-b2')] }, 'lrtosroute': { utils.ovn_name('lr-id-a'): ['20.0.0.0/16'], utils.ovn_name('lr-id-b'): ['10.0.0.0/16'] }, 'lrtonat': { utils.ovn_name('lr-id-a'): ['10.0.3.1'], utils.ovn_name('lr-id-b'): ['20.0.2.1', '20.0.2.4', '20.0.2.5'], }, 'lstoacl': { utils.ovn_name('ls-id-1'): [1, 2, 3, 4], utils.ovn_name('ls-id-2'): [5, 6], 'ls-id-4': [7, 8], utils.ovn_name('ls-id-5'): [9, 10] } } def setUp(self): super(TestNBImplIdlOvn, self).setUp() self.lswitch_table = fakes.FakeOvsdbTable.create_one_ovsdb_table() self.lsp_table = fakes.FakeOvsdbTable.create_one_ovsdb_table() self.lrouter_table = fakes.FakeOvsdbTable.create_one_ovsdb_table() self.lrp_table = fakes.FakeOvsdbTable.create_one_ovsdb_table() self.sroute_table = fakes.FakeOvsdbTable.create_one_ovsdb_table() self.nat_table = fakes.FakeOvsdbTable.create_one_ovsdb_table() self.acl_table = fakes.FakeOvsdbTable.create_one_ovsdb_table() self.dhcp_table = fakes.FakeOvsdbTable.create_one_ovsdb_table() self.address_set_table = fakes.FakeOvsdbTable.create_one_ovsdb_table() self._tables = {} self._tables['Logical_Switch'] = self.lswitch_table self._tables['Logical_Switch_Port'] = self.lsp_table self._tables['Logical_Router'] = self.lrouter_table self._tables['Logical_Router_Port'] = self.lrp_table self._tables['Logical_Router_Static_Route'] = self.sroute_table self._tables['ACL'] = self.acl_table self._tables['DHCP_Options'] = self.dhcp_table self._tables['Address_Set'] = self.address_set_table with mock.patch.object(impl_idl_ovn, 'get_connection', return_value=mock.Mock()): with mock.patch.object(ovs_idl.Backend, 'autocreate_indices', create=True): impl_idl_ovn.OvsdbNbOvnIdl.ovsdb_connection = None self.nb_ovn_idl = impl_idl_ovn.OvsdbNbOvnIdl(mock.Mock()) self.nb_ovn_idl.idl.tables = self._tables def _load_nb_db(self): # Load Switches and Switch Ports fake_lswitches = TestNBImplIdlOvn.fake_set['lswitches'] self._load_ovsdb_fake_rows(self.lswitch_table, fake_lswitches) fake_lsps = TestNBImplIdlOvn.fake_set['lswitch_ports'] self._load_ovsdb_fake_rows(self.lsp_table, fake_lsps) # Associate switches and ports self._construct_ovsdb_references( TestNBImplIdlOvn.fake_associations['lstolsp'], self.lswitch_table, self.lsp_table, 'name', 'name', 'ports') # Load Routers and Router Ports fake_lrouters = TestNBImplIdlOvn.fake_set['lrouters'] self._load_ovsdb_fake_rows(self.lrouter_table, fake_lrouters) fake_lrps = TestNBImplIdlOvn.fake_set['lrouter_ports'] self._load_ovsdb_fake_rows(self.lrp_table, fake_lrps) # Associate routers and router ports self._construct_ovsdb_references( TestNBImplIdlOvn.fake_associations['lrtolrp'], self.lrouter_table, self.lrp_table, 'name', 'name', 'ports') # Load static routes fake_sroutes = TestNBImplIdlOvn.fake_set['static_routes'] self._load_ovsdb_fake_rows(self.sroute_table, fake_sroutes) # Associate routers and static routes self._construct_ovsdb_references( TestNBImplIdlOvn.fake_associations['lrtosroute'], self.lrouter_table, self.sroute_table, 'name', 'ip_prefix', 'static_routes') # Load nats fake_nats = TestNBImplIdlOvn.fake_set['nats'] self._load_ovsdb_fake_rows(self.nat_table, fake_nats) # Associate routers and nats self._construct_ovsdb_references( TestNBImplIdlOvn.fake_associations['lrtonat'], self.lrouter_table, self.nat_table, 'name', 'external_ip', 'nat') # Load acls fake_acls = TestNBImplIdlOvn.fake_set['acls'] self._load_ovsdb_fake_rows(self.acl_table, fake_acls) # Associate switches and acls self._construct_ovsdb_references( TestNBImplIdlOvn.fake_associations['lstoacl'], self.lswitch_table, self.acl_table, 'name', 'unit_test_id', 'acls') # Load dhcp options fake_dhcp_options = TestNBImplIdlOvn.fake_set['dhcp_options'] self._load_ovsdb_fake_rows(self.dhcp_table, fake_dhcp_options) # Load address sets fake_address_sets = TestNBImplIdlOvn.fake_set['address_sets'] self._load_ovsdb_fake_rows(self.address_set_table, fake_address_sets) @mock.patch.object(ovs_idl.Backend, 'autocreate_indices', mock.Mock(), create=True) @mock.patch.object(impl_idl_ovn.OvsdbNbOvnIdl, 'ovsdb_connection', None) @mock.patch.object(impl_idl_ovn, 'get_connection', mock.Mock()) def test_setting_ovsdb_probe_timeout_default_value(self): inst = impl_idl_ovn.OvsdbNbOvnIdl(mock.Mock()) inst.idl._session.reconnect.set_probe_interval.assert_called_with( 60000) @mock.patch.object(ovs_idl.Backend, 'autocreate_indices', mock.Mock(), create=True) @mock.patch.object(impl_idl_ovn.OvsdbNbOvnIdl, 'ovsdb_connection', None) @mock.patch.object(impl_idl_ovn, 'get_connection', mock.Mock()) @mock.patch.object(ovn_conf, 'get_ovn_ovsdb_probe_interval') def test_setting_ovsdb_probe_timeout(self, mock_get_probe_interval): mock_get_probe_interval.return_value = 5000 inst = impl_idl_ovn.OvsdbNbOvnIdl(mock.Mock()) inst.idl._session.reconnect.set_probe_interval.assert_called_with(5000) def test_get_all_logical_switches_with_ports(self): # Test empty mapping = self.nb_ovn_idl.get_all_logical_switches_with_ports() self.assertItemsEqual(mapping, {}) # Test loaded values self._load_nb_db() mapping = self.nb_ovn_idl.get_all_logical_switches_with_ports() expected = [{ 'name': utils.ovn_name('ls-id-1'), 'ports': ['lsp-id-11', 'lsp-id-12', 'lsp-rp-id-1'], 'provnet_port': 'provnet-ls-id-1' }, { 'name': utils.ovn_name('ls-id-2'), 'ports': ['lsp-id-21', 'lsp-rp-id-2'], 'provnet_port': 'provnet-ls-id-2' }, { 'name': utils.ovn_name('ls-id-3'), 'ports': ['lsp-id-31', 'lsp-id-32', 'lsp-rp-id-3', 'lsp-vpn-id-3'], 'provnet_port': None }, { 'name': utils.ovn_name('ls-id-5'), 'ports': ['lsp-id-51', 'lsp-id-52', 'lsp-rp-id-5', 'lsp-vpn-id-5'], 'provnet_port': None }] self.assertItemsEqual(mapping, expected) def test_get_all_logical_routers_with_rports(self): # Test empty mapping = self.nb_ovn_idl.get_all_logical_switches_with_ports() self.assertItemsEqual(mapping, {}) # Test loaded values self._load_nb_db() mapping = self.nb_ovn_idl.get_all_logical_routers_with_rports() expected = [{ 'name': 'lr-id-a', 'ports': { 'orp-id-a1': ['10.0.1.0/24'], 'orp-id-a2': ['10.0.2.0/24'], 'orp-id-a3': ['10.0.3.0/24'] }, 'static_routes': [{ 'destination': '20.0.0.0/16', 'nexthop': '10.0.3.253' }], 'snats': [{ 'external_ip': '10.0.3.1', 'logical_ip': '20.0.0.0/16', 'type': 'snat' }], 'dnat_and_snats': [] }, { 'name': 'lr-id-b', 'ports': { 'xrp-id-b1': ['20.0.1.0/24'], 'orp-id-b2': ['20.0.2.0/24'] }, 'static_routes': [{ 'destination': '10.0.0.0/16', 'nexthop': '20.0.2.253' }], 'snats': [{ 'external_ip': '20.0.2.1', 'logical_ip': '10.0.0.0/24', 'type': 'snat' }], 'dnat_and_snats': [{ 'external_ip': '20.0.2.4', 'logical_ip': '10.0.0.4', 'type': 'dnat_and_snat' }, { 'external_ip': '20.0.2.5', 'logical_ip': '10.0.0.5', 'type': 'dnat_and_snat', 'external_mac': '00:01:02:03:04:05', 'logical_port': 'lsp-id-001' }] }, { 'name': 'lr-id-c', 'ports': {}, 'static_routes': [], 'snats': [], 'dnat_and_snats': [] }, { 'name': 'lr-id-d', 'ports': {}, 'static_routes': [], 'snats': [], 'dnat_and_snats': [] }, { 'name': 'lr-id-e', 'ports': {}, 'static_routes': [], 'snats': [], 'dnat_and_snats': [] }] self.assertItemsEqual(mapping, expected) def test_get_acls_for_lswitches(self): self._load_nb_db() # Test neutron switches lswitches = ['ls-id-1', 'ls-id-2', 'ls-id-3', 'ls-id-5'] acl_values, acl_objs, lswitch_ovsdb_dict = \ self.nb_ovn_idl.get_acls_for_lswitches(lswitches) excepted_acl_values = { 'lsp-id-11': [{ 'action': 'allow-related', 'lport': 'lsp-id-11', 'lswitch': 'neutron-ls-id-1', 'external_ids': { 'neutron:lport': 'lsp-id-11' }, 'direction': 'from-lport', 'match': 'inport == "lsp-id-11" && ip4' }, { 'action': 'allow-related', 'lport': 'lsp-id-11', 'lswitch': 'neutron-ls-id-1', 'external_ids': { 'neutron:lport': 'lsp-id-11' }, 'direction': 'to-lport', 'match': 'outport == "lsp-id-11" && ip4.src == $as_ip4_id_1' }], 'lsp-id-12': [{ 'action': 'allow-related', 'lport': 'lsp-id-12', 'lswitch': 'neutron-ls-id-1', 'external_ids': { 'neutron:lport': 'lsp-id-12' }, 'direction': 'from-lport', 'match': 'inport == "lsp-id-12" && ip4' }, { 'action': 'allow-related', 'lport': 'lsp-id-12', 'lswitch': 'neutron-ls-id-1', 'external_ids': { 'neutron:lport': 'lsp-id-12' }, 'direction': 'to-lport', 'match': 'outport == "lsp-id-12" && ip4.src == $as_ip4_id_1' }], 'lsp-id-21': [{ 'action': 'allow-related', 'lport': 'lsp-id-21', 'lswitch': 'neutron-ls-id-2', 'external_ids': { 'neutron:lport': 'lsp-id-21' }, 'direction': 'from-lport', 'match': 'inport == "lsp-id-21" && ip4' }, { 'action': 'allow-related', 'lport': 'lsp-id-21', 'lswitch': 'neutron-ls-id-2', 'external_ids': { 'neutron:lport': 'lsp-id-21' }, 'direction': 'to-lport', 'match': 'outport == "lsp-id-21" && ip4.src == $as_ip4_id_2' }], 'lsp-id-52': [{ 'action': 'allow-related', 'lport': 'lsp-id-52', 'lswitch': 'neutron-ls-id-5', 'external_ids': { 'neutron:lport': 'lsp-id-52' }, 'direction': 'from-lport', 'match': 'inport == "lsp-id-52" && ip4' }, { 'action': 'allow-related', 'lport': 'lsp-id-52', 'lswitch': 'neutron-ls-id-5', 'external_ids': { 'neutron:lport': 'lsp-id-52' }, 'direction': 'to-lport', 'match': 'outport == "lsp-id-52" && ip4.src == $as_ip4_id_5' }] } self.assertItemsEqual(acl_values, excepted_acl_values) self.assertEqual(len(acl_objs), 8) self.assertEqual(len(lswitch_ovsdb_dict), len(lswitches)) # Test non-neutron switches lswitches = ['ls-id-4'] acl_values, acl_objs, lswitch_ovsdb_dict = \ self.nb_ovn_idl.get_acls_for_lswitches(lswitches) self.assertItemsEqual(acl_values, {}) self.assertEqual(len(acl_objs), 0) self.assertEqual(len(lswitch_ovsdb_dict), 0) def test_get_all_chassis_gateway_bindings(self): self._load_nb_db() bindings = self.nb_ovn_idl.get_all_chassis_gateway_bindings() expected = { 'host-1': [ utils.ovn_lrouter_port_name('orp-id-a1'), utils.ovn_lrouter_port_name('orp-id-a2') ], 'host-2': [utils.ovn_lrouter_port_name('orp-id-b2')], ovn_const.OVN_GATEWAY_INVALID_CHASSIS: [utils.ovn_name('orp-id-a3')] } self.assertItemsEqual(bindings, expected) bindings = self.nb_ovn_idl.get_all_chassis_gateway_bindings([]) self.assertItemsEqual(bindings, expected) bindings = self.nb_ovn_idl.get_all_chassis_gateway_bindings(['host-1']) expected = { 'host-1': [ utils.ovn_lrouter_port_name('orp-id-a1'), utils.ovn_lrouter_port_name('orp-id-a2') ] } self.assertItemsEqual(bindings, expected) def test_get_gateway_chassis_binding(self): self._load_nb_db() chassis = self.nb_ovn_idl.get_gateway_chassis_binding( utils.ovn_lrouter_port_name('orp-id-a1')) self.assertEqual(chassis, ['host-1']) chassis = self.nb_ovn_idl.get_gateway_chassis_binding( utils.ovn_lrouter_port_name('orp-id-b2')) self.assertEqual(chassis, ['host-2']) chassis = self.nb_ovn_idl.get_gateway_chassis_binding( utils.ovn_lrouter_port_name('orp-id-a3')) self.assertEqual(chassis, ['neutron-ovn-invalid-chassis']) chassis = self.nb_ovn_idl.get_gateway_chassis_binding( utils.ovn_lrouter_port_name('orp-id-b3')) self.assertEqual([], chassis) chassis = self.nb_ovn_idl.get_gateway_chassis_binding('bad') self.assertEqual([], chassis) def test_get_unhosted_gateways(self): self._load_nb_db() # Port physnet-dict port_physnet_dict = { 'orp-id-a1': 'physnet1', # scheduled 'orp-id-a2': 'physnet1', # scheduled 'orp-id-a3': 'physnet1', # not scheduled 'orp-id-b6': 'physnet2' } # not scheduled # Test only that orp-id-a3 is to be scheduled. # Rest ports don't have required chassis (physnet2) # or are already scheduled. unhosted_gateways = self.nb_ovn_idl.get_unhosted_gateways( port_physnet_dict, { 'host-1': 'physnet1', 'host-2': 'physnet3' }, ['host-1', 'host-2']) expected = ['lrp-orp-id-a3'] self.assertItemsEqual(unhosted_gateways, expected) # Test both host-1, host-2 in valid list unhosted_gateways = self.nb_ovn_idl.get_unhosted_gateways( port_physnet_dict, { 'host-1': 'physnet1', 'host-2': 'physnet2' }, ['host-1', 'host-2']) expected = ['lrp-orp-id-a3', 'lrp-orp-id-b6'] self.assertItemsEqual(unhosted_gateways, expected) def test_get_unhosted_gateways_deleted_physnet(self): self._load_nb_db() # The LRP is on host-2 now router_row = self._find_ovsdb_fake_row(self.lrp_table, 'name', 'lrp-orp-id-a1') setattr(router_row, 'options', {ovn_const.OVN_GATEWAY_CHASSIS_KEY: 'host-2'}) port_physnet_dict = {'orp-id-a1': 'physnet1'} # Lets spoof that physnet1 is deleted from host-2. unhosted_gateways = self.nb_ovn_idl.get_unhosted_gateways( port_physnet_dict, { 'host-1': 'physnet1', 'host-2': 'physnet3' }, ['host-1', 'host-2']) # Make sure that lrp is rescheduled, because host-1 has physet1 expected = ['lrp-orp-id-a1'] self.assertItemsEqual(unhosted_gateways, expected) # Spoof that there is no valid host with required physnet. unhosted_gateways = self.nb_ovn_idl.get_unhosted_gateways( port_physnet_dict, { 'host-1': 'physnet4', 'host-2': 'physnet3' }, ['host-1', 'host-2']) self.assertItemsEqual(unhosted_gateways, []) def _test_get_unhosted_gateway_max_chassis(self, r): gw_chassis_table = fakes.FakeOvsdbTable.create_one_ovsdb_table() self._tables['Gateway_Chassis'] = gw_chassis_table gw_chassis = collections.namedtuple('gw_chassis', 'chassis_name priority') TestNBImplIdlOvn.fake_set['lrouter_ports'][0]['gateway_chassis'] = [ gw_chassis(chassis_name='host-%s' % x, priority=x) for x in r ] self._load_nb_db() self.port_physnet_dict = {'orp-id-a1': 'physnet1'} def test_get_unhosted_gateway_max_chassis_lack_of_chassis(self): self._test_get_unhosted_gateway_max_chassis(r=(1, 3, 5)) unhosted_gateways = self.nb_ovn_idl.get_unhosted_gateways( self.port_physnet_dict, { 'host-1': 'physnet1', 'host-2': 'physnet2', 'host-3': 'physnet1', 'host-4': 'physnet2', 'host-5': 'physnet1', 'host-6': 'physnet2' }, ['host-%s' % x for x in range(1, 7)]) # We don't have required number of chassis expected = [] self.assertItemsEqual(unhosted_gateways, expected) def test_get_unhosted_gateway_max_chassis(self): # We have required number of chassis, and lrp # is hosted everywhere. self._test_get_unhosted_gateway_max_chassis(r=range(1, 6)) unhosted_gateways = self.nb_ovn_idl.get_unhosted_gateways( self.port_physnet_dict, { 'host-1': 'physnet1', 'host-2': 'physnet1', 'host-3': 'physnet1', 'host-4': 'physnet1', 'host-5': 'physnet1', 'host-6': 'physnet1' }, ['host-%s' % x for x in range(1, 7)]) expected = [] self.assertItemsEqual(unhosted_gateways, expected) def test_get_unhosed_gateway_schedule_to_max(self): # The LRP is not yet scheduled on all chassis # but we can schedule on new chassis now. self._test_get_unhosted_gateway_max_chassis(r=range(1, 4)) unhosted_gateways = self.nb_ovn_idl.get_unhosted_gateways( self.port_physnet_dict, { 'host-1': 'physnet1', 'host-2': 'physnet1', 'host-3': 'physnet1', 'host-4': 'physnet1', 'host-5': 'physnet1', 'host-6': 'physnet1' }, ['host-%s' % x for x in range(1, 7)]) expected = ['lrp-orp-id-a1'] self.assertItemsEqual(unhosted_gateways, expected) def test_get_subnet_dhcp_options(self): self._load_nb_db() subnet_options = self.nb_ovn_idl.get_subnet_dhcp_options( 'subnet-id-10-0-2-0') expected_row = self._find_ovsdb_fake_row(self.dhcp_table, 'cidr', '10.0.2.0/24') self.assertEqual( { 'subnet': { 'cidr': expected_row.cidr, 'external_ids': expected_row.external_ids, 'options': expected_row.options, 'uuid': expected_row.uuid }, 'ports': [] }, subnet_options) subnet_options = self.nb_ovn_idl.get_subnet_dhcp_options( 'subnet-id-11-0-2-0')['subnet'] self.assertIsNone(subnet_options) subnet_options = self.nb_ovn_idl.get_subnet_dhcp_options( 'port-id-30-0-1-0')['subnet'] self.assertIsNone(subnet_options) def test_get_subnet_dhcp_options_with_ports(self): # Test empty subnet_options = self.nb_ovn_idl.get_subnet_dhcp_options( 'subnet-id-10-0-1-0', with_ports=True) self.assertItemsEqual({'subnet': None, 'ports': []}, subnet_options) # Test loaded values self._load_nb_db() # Test getting both subnet and port dhcp options subnet_options = self.nb_ovn_idl.get_subnet_dhcp_options( 'subnet-id-10-0-1-0', with_ports=True) dhcp_rows = [ self._find_ovsdb_fake_row(self.dhcp_table, 'cidr', '10.0.1.0/24'), self._find_ovsdb_fake_row(self.dhcp_table, 'cidr', '10.0.1.0/26') ] expected_rows = [{ 'cidr': dhcp_row.cidr, 'external_ids': dhcp_row.external_ids, 'options': dhcp_row.options, 'uuid': dhcp_row.uuid } for dhcp_row in dhcp_rows] self.assertItemsEqual(expected_rows, [subnet_options['subnet']] + subnet_options['ports']) # Test getting only subnet dhcp options subnet_options = self.nb_ovn_idl.get_subnet_dhcp_options( 'subnet-id-10-0-2-0', with_ports=True) dhcp_rows = [ self._find_ovsdb_fake_row(self.dhcp_table, 'cidr', '10.0.2.0/24') ] expected_rows = [{ 'cidr': dhcp_row.cidr, 'external_ids': dhcp_row.external_ids, 'options': dhcp_row.options, 'uuid': dhcp_row.uuid } for dhcp_row in dhcp_rows] self.assertItemsEqual(expected_rows, [subnet_options['subnet']] + subnet_options['ports']) # Test getting no dhcp options subnet_options = self.nb_ovn_idl.get_subnet_dhcp_options( 'subnet-id-11-0-2-0', with_ports=True) self.assertItemsEqual({'subnet': None, 'ports': []}, subnet_options) def test_get_subnets_dhcp_options(self): self._load_nb_db() def get_row_dict(row): return { 'cidr': row.cidr, 'external_ids': row.external_ids, 'options': row.options, 'uuid': row.uuid } subnets_options = self.nb_ovn_idl.get_subnets_dhcp_options( ['subnet-id-10-0-1-0', 'subnet-id-10-0-2-0']) expected_rows = [ get_row_dict( self._find_ovsdb_fake_row(self.dhcp_table, 'cidr', cidr)) for cidr in ('10.0.1.0/24', '10.0.2.0/24') ] self.assertItemsEqual(expected_rows, subnets_options) subnets_options = self.nb_ovn_idl.get_subnets_dhcp_options( ['subnet-id-11-0-2-0', 'subnet-id-20-0-1-0']) expected_row = get_row_dict( self._find_ovsdb_fake_row(self.dhcp_table, 'cidr', '20.0.1.0/24')) self.assertItemsEqual([expected_row], subnets_options) subnets_options = self.nb_ovn_idl.get_subnets_dhcp_options( ['port-id-30-0-1-0', 'fake-not-exist']) self.assertEqual([], subnets_options) def test_get_all_dhcp_options(self): self._load_nb_db() dhcp_options = self.nb_ovn_idl.get_all_dhcp_options() self.assertEqual(len(dhcp_options['subnets']), 3) self.assertEqual(len(dhcp_options['ports_v4']), 2) def test_get_address_sets(self): self._load_nb_db() address_sets = self.nb_ovn_idl.get_address_sets() self.assertEqual(len(address_sets), 4) def test_get_port_group_not_supported(self): self._load_nb_db() # Make sure that PG tables doesn't exist in fake db. self._tables.pop('Port_Group', None) port_group = self.nb_ovn_idl.get_port_group(str(uuid.uuid4())) self.assertIsNone(port_group) def test_get_port_groups_not_supported(self): self._load_nb_db() # Make sure that PG tables doesn't exist in fake db. self._tables.pop('Port_Group', None) port_groups = self.nb_ovn_idl.get_port_groups() self.assertEqual({}, port_groups)