def metadata(self, resource_type: str, os_id: str) -> PolicyResourceMeta: if resource_type == Provider.SG_RULES: with LockManager.get_lock(Provider.SG_RULES): meta = self._metadata[Provider.SG_RULES].meta.get(os_id) if meta: if not meta.rules: meta.rules = self._fetch_rules_from_nsx(meta) return meta with LockManager.get_lock(resource_type): return self._metadata[resource_type].meta.get(os_id)
def metadata(self, resource_type, os_id) -> ResourceMeta: if resource_type == Provider.SG_RULE: with LockManager.get_lock(Provider.SG_RULES): meta = self._metadata[Provider.SG_RULES].meta.get(os_id) if meta: rules = self.client.get_all(API.RULES.format(meta.id)) meta = {Resource(o).os_id: o for o in rules} return meta with LockManager.get_lock(resource_type): return self._metadata[resource_type].meta.get(os_id)
def wrapper(self, *args, **kwargs): with LockManager.get_lock(API.MIGR_UNIT): initiated = False try: m_data: Payload.MigrationData = build_migr_data_func( self, *args, **kwargs) json_migdata = m_data.json() if m_data else None if not json_migdata: LOG.warn( "No migration data provided. Migration skiped.") return self._initiate_migration() initiated = True self._set_migration(migr_data=json_migdata) self._start_migration(migr_data=json_migdata) LOG.debug("Pre-checking migration ...") self._precheck_migration(migr_data=json_migdata) self._continue_migration(migr_data=json_migdata) LOG.debug("Post-checking migration ...") self._await_migration(migr_data=json_migdata) LOG.info("Migration completed.") finally: if initiated: self._end_migration() return m_data
def security_group_members(self, os_id: str, reference=False): """ Realize security group members state. Realization will happen only if the group has active ports on the host or if it used as remote security group by a group having such ports. :os_id: -- OpenStack ID of the Security Group :reference: -- if True will create the group if unknown by the provider """ with LockManager.get_lock("member-{}".format(os_id)): pp = self.plcy_provider meta = pp.metadata(pp.SG_MEMBERS, os_id) if not (reference and meta): if self.rpc.has_security_group_used_by_host(os_id): cidrs = self.rpc.get_security_group_members_effective_ips( os_id) port_ids = set(self.rpc.get_security_group_port_ids(os_id)) segment_ports = pp.get_port_meta_by_ids(port_ids) paths = [p.path for p in segment_ports] # SG Members are not revisionable, use default "0" pp.sg_members_realize({ "id": os_id, "cidrs": cidrs, "revision_number": "0", "member_paths": paths }) else: pp.sg_members_realize({"id": os_id}, delete=True)
def _login(self): LOG.info("Session token - acquiring") now = int(time.time()) with LockManager.get_lock(self._base_path): if now > self._login_timestamp: resp = requests.post( **self._params(path=self._login_path, data=self._login_data, verify=self._session.verify)) resp.raise_for_status() self._session.headers["Cookie"] = \ resp.headers.get("Set-Cookie") self._session.headers["X-XSRF-TOKEN"] = \ resp.headers.get("X-XSRF-TOKEN") self._session.headers["Accept"] = "application/json" self._session.headers["Content-Type"] = "application/json" self._login_timestamp = int(time.time()) try: # Refresh version after login self.version(refresh=True) except Exception: pass LOG.info("Session token - acquired, connected to NSX-T {}".format( self._version))
def security_group_rules(self, os_id: str): """ Realize security group rules state. Realization will happen only if the group has active ports on the host. :os_id: -- OpenStack ID of the Security Group """ with LockManager.get_lock("rules-{}".format(os_id)): os_sg = self.rpc.get_security_group(os_id) if os_sg and os_sg.get("ports"): # Create Members Container self.security_group_members(os_id, reference=True) os_sg["rules"] = self.rpc.get_rules_for_security_group_id( os_id) for os_rule in os_sg["rules"]: remote_id = os_rule.get("remote_group_id") if remote_id: self.security_group_members(remote_id, reference=True) logged = self.rpc.has_security_group_logging(os_id) LOG.info( f"Neutron DB logged flag for {os_id}: rpc.has_security_group_logging(os_id): {logged}" ) self.plcy_provider.sg_rules_realize(os_sg, logged=logged) else: self.plcy_provider.sg_rules_realize({"id": os_id}, delete=True)
def metadata_refresh(self, resource_type, params=dict()): provider = self._metadata[resource_type] with provider.meta: LOG.info("[%s] Fetching Policy NSX-T metadata for Type:%s.", self.provider, resource_type) endpoint = provider.endpoint if resource_type == Provider.SEGM_PORT: endpoint = API.SEARCH_QUERY params = API.SEARCH_Q_SEG_PORTS if resource_type == Provider.SEGM_QOS: endpoint = API.SEARCH_QUERY params = API.SEARCH_Q_QOS_PROFILES resources = self.client.get_all(path=endpoint, params=params) with LockManager.get_lock(resource_type): provider.meta.reset() for o in resources: res = Resource(o) if not res.is_managed: continue if resource_type == Provider.SG_RULES and not res.has_valid_os_uuid: continue if resource_type == Provider.SG_MEMBERS and NSXV3_REVISION_SCOPE not in res.tags: continue if resource_type == Provider.SG_RULES_REMOTE_PREFIX and NSXV3_REVISION_SCOPE in res.tags: continue if resource_type == Provider.SEGM_PORT and not self._is_valid_vlan( res): continue provider.meta.add(res)
def metadata_update(self, resource_type, provider_object) -> PolicyResourceMeta: if resource_type != Provider.SG_RULE: with LockManager.get_lock(resource_type): res = Resource(provider_object) self._metadata[resource_type].meta.update(res) return res.meta
def update_policy_logging(self, log_obj: dict): """ Realize security policy logging state update. :os_seg_id: -- OpenStack Security Group ID :return: -- None """ with LockManager.get_lock("rules-{}".format(log_obj['resource_id'])): self.plcy_provider.update_policy_logging(log_obj)
def get_port_meta_by_ids(self, port_ids: Set[str]) -> Set[PolicyResourceMeta]: segment_ports = set() with LockManager.get_lock(self.SEGM_PORT): keys = set(self._metadata[self.SEGM_PORT].meta.keys()) segment_ports.update([ self._metadata[self.SEGM_PORT].meta.meta.get(id) for id in keys.intersection(port_ids) ]) return segment_ports
def network(self, os_seg_id: str): """ Realize Network state. :os_seg_id: -- OpenStack Network Segmentation ID :return: -- provider ID for the network """ with LockManager.get_lock("network-{}".format(os_seg_id)): meta = self._network_realize(os_seg_id) return { "nsx-logical-switch-id": meta.id, "external-id": meta.id, "segmentation_id": os_seg_id }
def check_service_availability(self): with LockManager.get_lock(API.MIGR_UNIT): try: self.client.post(path=API.MP_TO_POLICY, data={}).raise_for_status() self.client.post(path=API.MIGRATION_ABORT, data=None).raise_for_status() stat = self.client.get(path=API.SERVICE_STATUS) srvc_stat = stat.json() if not srvc_stat or not srvc_stat.get("enabled"): raise RuntimeError(f"{stat.content}") except Exception as e: raise RuntimeError( f"MP-TO-POLICY API not enabled or service down. ({e})")
def port(self, os_id: str): """ Realize port state. :os_id: -- OpenStack ID of the Port """ with LockManager.get_lock("port-{}".format(os_id)): port: dict = self.rpc.get_port(os_id) if port: os_qid = port.get("qos_policy_id") if os_qid: self.qos(os_qid, reference=True) self._port_realize(port) else: self._port_realize({"id": os_id}, delete=True)
def precreate_port(self, os_id: str, network_meta: dict): """ Try to precreate port on first binding request. :os_id: -- OpenStack ID of the Port :network_meta: -- NSX Switch metadata """ with LockManager.get_lock("port-{}".format(os_id)): port: dict = self.rpc.get_port(os_id) if port: os_qid = port.get("qos_policy_id") if os_qid: self.qos(os_qid, reference=True) if not port.get("vif_details") and network_meta: port["vif_details"] = network_meta self._port_realize(port)
def realize_sg_members_after_port_realization( self, port_sgs: List[str], port_meta: PolicyResourceMeta): for sg_id in port_sgs: with LockManager.get_lock("member-{}".format(sg_id)): sg_meta = self.metadata(self.SG_MEMBERS, sg_id) if not sg_meta: raise RuntimeError( f"Not found SG Metadata for security_group: {sg_id}") if not port_meta.path: raise RuntimeError( f"Not found path in Metadata for port: {port_meta.id}") if port_meta.path not in sg_meta.sg_members: sg_meta.sg_members.append(port_meta.path) self.sg_members_realize({ "id": sg_id, "cidrs": sg_meta.sg_cidrs, "member_paths": sg_meta.sg_members, "revision_number": "0" })
def qos(self, os_id: str, reference=False): """ Realize QoS Policy state. :os_id: -- OpenStack ID of the QoS Policy :reference: -- If True will create policy if unknown by the provider """ with LockManager.get_lock("qos-{}".format(os_id)): plcy_meta = self.plcy_provider.metadata( self.plcy_provider.SEGM_QOS, os_id) mgr_meta = self.mngr_provider.metadata(self.mngr_provider.QOS, os_id) if not (reference and mgr_meta): qos = self.rpc.get_qos(os_id) if qos: self._qos_realize(os_qos=qos, is_plcy=bool(plcy_meta), is_mngr=bool(mgr_meta)) else: self._qos_realize(os_qos={"id": os_id}, is_plcy=bool(plcy_meta), is_mngr=bool(mgr_meta), delete=True)
def _get_sg_provider_rule(self, os_rule: dict, revision: int) -> dict: provider_rule = dict() if os_rule.get("remote_ip_prefix"): net = netaddr.IPNetwork(os_rule["remote_ip_prefix"], flags=netaddr.NOHOST) meta_addr = [netaddr.IPAddress("0.0.0.0"), netaddr.IPAddress("::")] if net.ip in meta_addr: cidr = str(net) with LockManager.get_lock(cidr): meta = self.metadata(Provider.SG_RULES_REMOTE_PREFIX, cidr) if not meta: o = self._create_sg_provider_rule_remote_prefix(cidr) meta = self.metadata_update( Provider.SG_RULES_REMOTE_PREFIX, o) provider_rule["remote_ip_prefix_id"] = meta.id elif os_rule.get("remote_group_id"): meta = self.metadata(Provider.SG_MEMBERS, os_rule["remote_group_id"]) if meta: provider_rule["remote_group_id"] = meta.id provider_rule["_revision"] = revision return provider_rule
def metadata_refresh(self, resource_type, params=dict()): if resource_type != Provider.SG_RULE: provider = self._metadata[resource_type] with provider.meta: LOG.info("[%s] Fetching NSX-T metadata for Type:%s.", self.provider, resource_type) if provider.endpoint == API.PROFILES: params = API.PARAMS_GET_QOS_PROFILES resources = self.client.get_all(path=provider.endpoint, params=params) with LockManager.get_lock(resource_type): provider.meta.reset() for o in resources: res = Resource(o) if not res.is_managed: continue if resource_type == Provider.SG_MEMBERS: if NSXV3_REVISION_SCOPE not in res.tags: continue if resource_type == Provider.SG_RULES_REMOTE_PREFIX: if NSXV3_REVISION_SCOPE in res.tags: continue if resource_type == Provider.SG_RULES: if not res.has_valid_os_uuid: continue if resource_type == Provider.PORT: # Ensure this port is attached to a agent managed # logical switch, else skip it is_valid_vlan = False for name, ls in self._metadata[Provider.NETWORK].meta.meta.items(): if ls.id == res.resource.get("logical_switch_id") and name.isnumeric(): is_valid_vlan = True break if not is_valid_vlan: continue provider.meta.add(res)
def all(self, dryrun=False): """ Enforce desired state between OpenStack and Provider objects Objects concidered outdated include new, updated or removed :force: bool -- if True concider all objects as outdated """ with LockManager.get_lock("all"): if self.kpi().get("passive") > 0: return _slice = cfg.CONF.AGENT.synchronization_queue_size pp = self.plcy_provider mp = self.mngr_provider r = self.rpc port_meta = self._os_meta(r.get_ports_with_revisions) sg_meta = self._os_meta(r.get_security_groups_with_revisions) qos_meta = self._os_meta(r.get_qos_policies_with_revisions) # Force networks refresh, only mp.metadata_refresh(mp.NETWORK) pp.metadata_refresh(pp.SEGMENT) # Refresh entire metadata with its latest state LOG.info("Inventory metadata is going to be refreshed.") seg_port_outdated, seg_port_current = pp.outdated( pp.SEGM_PORT, port_meta) port_outdated, port_current = mp.outdated(mp.PORT, port_meta) sgr_outdated, sgr_current = pp.outdated(pp.SG_RULES, sg_meta) qos_outdated, qos_current = mp.outdated(mp.QOS, qos_meta) seg_qos_outdated, seg_qos_current = pp.outdated( pp.SEGM_QOS, qos_meta) # Remove duplicated policy/manager objects seg_port_outdated = seg_port_outdated.difference(port_outdated) seg_port_current = seg_port_current.difference(port_current) seg_qos_outdated = seg_qos_outdated.difference(qos_outdated) seg_qos_current = seg_qos_current.difference(qos_current) # There is not way to revision group members but can 'age' them sgm_outdated, sgm_maybe_orphans = pp.outdated( pp.SG_MEMBERS, {sg: 0 for sg in sg_meta}) LOG.info("Inventory metadata have been refreshed.") if dryrun: LOG.info("Dryrun:%s. Metadata refresh completed.", dryrun) return # Don't count ports into synchronization limit, since they could exhaust the worker queue # and cause the agent to be stuck. outdated = list(itertools.islice(port_outdated, _slice)) _slice -= len(outdated) LOG.info("Realizing %s/%s resources of Type:Ports", len(outdated), len(port_outdated)) self.callback(outdated, self.port) if _slice <= 0: return outdated = list(itertools.islice(seg_port_outdated, _slice)) _slice -= len(outdated) LOG.info("Realizing %s/%s resources of Type:SegmentPorts", len(outdated), len(seg_port_outdated)) self.callback(outdated, self.port) if _slice <= 0: return outdated = list(itertools.islice(sgr_outdated, _slice)) _slice -= len(outdated) LOG.info("Realizing %s/%s resources of Type:Security Group Rules", len(outdated), len(sgr_outdated)) self.callback(outdated, self.security_group_rules) if _slice <= 0: return # sgm_outdated only includes missing objects, orphans are removed by ageing outdated = list(itertools.islice(sgm_outdated, _slice)) _slice -= len(outdated) LOG.info( "Realizing %s/%s resources of Type:Security Group Members", len(outdated), len(sgm_outdated)) self.callback(outdated, self.security_group_members) if _slice <= 0: return outdated = list(itertools.islice(qos_outdated, _slice)) _slice -= len(outdated) LOG.info("Realizing %s/%s resources of Type:QoS", len(outdated), len(qos_outdated)) self.callback(outdated, self.qos) if _slice <= 0: return outdated = list(itertools.islice(seg_qos_outdated, _slice)) _slice -= len(outdated) LOG.info("Realizing %s/%s resources of Type:SegmentQoS", len(outdated), len(seg_qos_outdated)) self.callback(outdated, self.qos) if _slice <= 0: return return self._age_cycle(_slice, seg_port_current, port_current, sgr_current, seg_qos_current, qos_current, sgm_maybe_orphans)
def metadata_delete(self, resource_type, os_id): if resource_type != Provider.SG_RULE: with LockManager.get_lock(resource_type): self._metadata[resource_type].meta.rm(os_id)
def metadata_delete(self, resource_type: str, os_id: str) -> None: with LockManager.get_lock(resource_type): self._metadata[resource_type].meta.rm(os_id)