def override_clients(self, new_clients): """ Replaces the service clients with a new set of them... and makes sure they go to the right places """ #shortcut names for the import sub-services if hasattr(self.clients, "resource_registry"): self.RR = self.clients.resource_registry if hasattr(self.clients, "instrument_management"): self.IMS = self.clients.instrument_management #farm everything out to the impls self.org = OrgImpl(self.clients) self.observatory = ObservatoryImpl(self.clients) self.subsite = SubsiteImpl(self.clients) self.platform_site = PlatformSiteImpl(self.clients) self.instrument_site = InstrumentSiteImpl(self.clients) self.instrument_device = InstrumentDeviceImpl(self.clients) self.platform_device = PlatformDeviceImpl(self.clients)
class ObservatoryManagementService(BaseObservatoryManagementService): def on_init(self): IonObject("Resource") # suppress pyflakes error CFG, log, RT, PRED, LCS, LCE, NotFound, BadRequest, log #suppress pyflakes errors about "unused import" self.override_clients(self.clients) self.HIERARCHY_DEPTH = {RT.InstrumentSite: 3, RT.PlatformSite: 2, RT.Subsite: 1, RT.Observatory: 0, } self.HIERARCHY_LOOKUP = [RT.Observatory, RT.Subsite, RT.PlatformSite, RT.InstrumentSite] def override_clients(self, new_clients): """ Replaces the service clients with a new set of them... and makes sure they go to the right places """ #shortcut names for the import sub-services if hasattr(self.clients, "resource_registry"): self.RR = self.clients.resource_registry if hasattr(self.clients, "instrument_management"): self.IMS = self.clients.instrument_management if hasattr(self.clients, "data_process_management"): self.PRMS = self.clients.data_process_management #farm everything out to the impls self.observatory = ObservatoryImpl(self.clients) self.subsite = SubsiteImpl(self.clients) self.platform_site = PlatformSiteImpl(self.clients) self.instrument_site = InstrumentSiteImpl(self.clients) self.instrument_device = InstrumentDeviceImpl(self.clients) self.platform_device = PlatformDeviceImpl(self.clients) ########################################################################## # # CRUD OPS # ########################################################################## def create_marine_facility(self, org=None): """Create an Org (domain of authority) that realizes a marine facility. This Org will have set up roles for a marine facility. Shared resources, such as a device can only be registered in one marine facility Org, and additionally in many virtual observatory Orgs. The marine facility operators will have more extensive permissions and will supercede virtual observatory commands @param org Org @retval org_id str @throws BadRequest if object does not have _id or _rev attribute @throws NotFound object with specified id does not exist """ log.debug("ObservatoryManagementService.create_marine_facility(): %s" % org) # create the org org.org_type = OrgTypeEnum.MARINE_FACILITY org_id = self.clients.org_management.create_org(org) #Instantiate initial set of User Roles for this marine facility instrument_operator_role = IonObject(RT.UserRole, name=INSTRUMENT_OPERATOR_ROLE, label='Instrument Operator', description='Marine Facility Instrument Operator') self.clients.org_management.add_user_role(org_id, instrument_operator_role) observatory_operator_role = IonObject(RT.UserRole, name=OBSERVATORY_OPERATOR_ROLE, label='Observatory Operator', description='Marine Facility Observatory Operator') self.clients.org_management.add_user_role(org_id, observatory_operator_role) data_operator_role = IonObject(RT.UserRole, name=DATA_OPERATOR_ROLE, label='Data Operator', description='Marine Facility Data Operator') self.clients.org_management.add_user_role(org_id, data_operator_role) return org_id def create_virtual_observatory(self, org=None): """Create an Org (domain of authority) that realizes a virtual observatory. This Org will have set up roles for a virtual observatory. Shared resources, such as a device can only be registered in one marine facility Org, and additionally in many virtual observatory Orgs. The marine facility operators will have more extensive permissions and will supercede virtual observatory commands @param org Org @retval org_id str @throws BadRequest if object does not have _id or _rev attribute @throws NotFound object with specified id does not exist """ log.debug("ObservatoryManagementService.create_virtual_observatory(): %s" % org) # create the org org.org_type = OrgTypeEnum.VIRTUAL_OBSERVATORY org_id = self.clients.org_management.create_org(org) return org_id def create_observatory(self, observatory=None): """Create a Observatory resource. An observatory is coupled with one Org. The Org is created and associated as part of this call. @param observatory Observatory @retval observatory_id str @throws BadRequest if object does not have _id or _rev attribute @throws NotFound object with specified id does not exist """ # create the marine facility observatory_id = self.observatory.create_one(observatory) return observatory_id def read_observatory(self, observatory_id=''): """Read a Observatory resource @param observatory_id str @retval observatory Observatory @throws NotFound object with specified id does not exist """ return self.observatory.read_one(observatory_id) def update_observatory(self, observatory=None): """Update a Observatory resource @param observatory Observatory @throws NotFound object with specified id does not exist """ return self.observatory.update_one(observatory) def delete_observatory(self, observatory_id=''): """Delete a Observatory resource @param observatory_id str @throws NotFound object with specified id does not exist """ return self.observatory.delete_one(observatory_id) def create_subsite(self, subsite=None, parent_id=''): """Create a Subsite resource. A subsite is a frame of reference within an observatory. Its parent is either the observatory or another subsite. @param subsite Subsite @param parent_id str @retval subsite_id str @throws BadRequest if object does not have _id or _rev attribute @throws NotFound object with specified id does not exist """ subsite_id = self.subsite.create_one(subsite) if parent_id: self.subsite.link_parent(subsite_id, parent_id) return subsite_id def read_subsite(self, subsite_id=''): """Read a Subsite resource @param subsite_id str @retval subsite Subsite @throws NotFound object with specified id does not exist """ return self.subsite.read_one(subsite_id) def update_subsite(self, subsite=None): """Update a Subsite resource @param subsite Subsite @throws NotFound object with specified id does not exist """ return self.subsite.update_one(subsite) def delete_subsite(self, subsite_id=''): """Delete a subsite resource, removes assocations to parents @param subsite_id str @throws NotFound object with specified id does not exist """ self.subsite.delete_one(subsite_id) def create_platform_site(self, platform_site=None, parent_id=''): """Create a PlatformSite resource. A platform_site is a frame of reference within an observatory. Its parent is either the observatory or another platform_site. @param platform_site PlatformSite @param parent_id str @retval platform_site_id str @throws BadRequest if object does not have _id or _rev attribute @throws NotFound object with specified id does not exist """ platform_site_id = self.platform_site.create_one(platform_site) if parent_id: self.platform_site.link_parent(platform_site_id, parent_id) return platform_site_id def read_platform_site(self, platform_site_id=''): """Read a PlatformSite resource @param platform_site_id str @retval platform_site PlatformSite @throws NotFound object with specified id does not exist """ return self.platform_site.read_one(platform_site_id) def update_platform_site(self, platform_site=None): """Update a PlatformSite resource @param platform_site PlatformSite @throws NotFound object with specified id does not exist """ return self.platform_site.update_one(platform_site) def delete_platform_site(self, platform_site_id=''): """Delete a PlatformSite resource, removes assocations to parents @param platform_site_id str @throws NotFound object with specified id does not exist """ self.platform_site.delete_one(platform_site_id) def create_instrument_site(self, instrument_site=None, parent_id=''): """Create a InstrumentSite resource. A instrument_site is a frame of reference within an observatory. Its parent is either the observatory or another instrument_site. @param instrument_site InstrumentSite @param parent_id str @retval instrument_site_id str @throws BadRequest if object does not have _id or _rev attribute @throws NotFound object with specified id does not exist """ instrument_site_id = self.instrument_site.create_one(instrument_site) if parent_id: self.instrument_site.link_parent(instrument_site_id, parent_id) return instrument_site_id def read_instrument_site(self, instrument_site_id=''): """Read a InstrumentSite resource @param instrument_site_id str @retval instrument_site InstrumentSite @throws NotFound object with specified id does not exist """ return self.instrument_site.read_one(instrument_site_id) def update_instrument_site(self, instrument_site=None): """Update a InstrumentSite resource @param instrument_site InstrumentSite @throws NotFound object with specified id does not exist """ return self.instrument_site.update_one(instrument_site) def delete_instrument_site(self, instrument_site_id=''): """Delete a InstrumentSite resource, removes assocations to parents @param instrument_site_id str @throws NotFound object with specified id does not exist """ # todo: give InstrumentSite a lifecycle in COI so that we can remove the "True" argument here self.instrument_site.delete_one(instrument_site_id) def create_deployment(self, deployment=None, site_id="", device_id=""): """ Create a Deployment resource. Represents a (possibly open-ended) time interval grouping one or more resources within a given context, such as an instrument deployment on a platform at an observatory site. """ deployment_id, version = self.clients.resource_registry.create(deployment) #Verify that site and device exist, add links if they do if site_id: site_obj = self.clients.resource_registry.read(site_id) if site_obj: self.clients.resource_registry.create_association(site_id, PRED.hasDeployment, deployment_id) if device_id: device_obj = self.clients.resource_registry.read(device_id) if device_obj: self.clients.resource_registry.create_association(device_id, PRED.hasDeployment, deployment_id) return deployment_id def update_deployment(self, deployment=None): # Overwrite Deployment object self.clients.resource_registry.update(deployment) def read_deployment(self, deployment_id=''): # Read Deployment object with _id matching id log.debug("Reading Deployment object id: %s" % deployment_id) deployment_obj = self.clients.resource_registry.read(deployment_id) return deployment_obj def delete_deployment(self, deployment_id=''): """ Delete a Deployment resource """ #Verify that the deployment exist deployment_obj = self.clients.resource_registry.read(deployment_id) if not deployment_obj: raise NotFound("Deployment %s does not exist" % deployment_id) # Remove the link between the Stream Definition resource and the Data Process Definition resource associations = self.clients.resource_registry.find_associations(None, PRED.hasDeployment, deployment_id, id_only=True) if not associations: raise NotFound("No Sites or Devices associated with this Deployment identifier " + str(deployment_id)) for association in associations: self.clients.resource_registry.delete_association(association) # Delete the deployment self.clients.resource_registry.delete(deployment_id) ############################ # # ASSOCIATIONS # ############################ def assign_site_to_site(self, child_site_id='', parent_site_id=''): """Connects a child site (any subtype) to a parent site (any subtype) @param child_site_id str @param parent_site_id str @throws NotFound object with specified id does not exist """ parent_site_obj = self.subsite.read_one(parent_site_id) parent_site_type = parent_site_obj._get_type() if RT.Observatory == parent_site_type: self.observatory.link_site(parent_site_id, child_site_id) elif RT.Subsite == parent_site_type: self.subsite.link_site(parent_site_id, child_site_id) elif RT.PlatformSite == parent_site_type: self.platform_site.link_site(parent_site_id, child_site_id) else: raise BadRequest("Tried to assign a child site to a %s resource" % parent_site_type) def unassign_site_from_site(self, child_site_id='', parent_site_id=''): """Disconnects a child site (any subtype) from a parent site (any subtype) @param child_site_id str @param parent_site_id str @throws NotFound object with specified id does not exist """ parent_site_obj = self.subsite.read_one(parent_site_id) parent_site_type = parent_site_obj._get_type() if RT.Observatory == parent_site_type: self.observatory.unlink_site(parent_site_id, child_site_id) elif RT.Subsite == parent_site_type: self.subsite.unlink_site(parent_site_id, child_site_id) elif RT.PlatformSite == parent_site_type: self.platform_site.unlink_site(parent_site_id, child_site_id) else: raise BadRequest("Tried to unassign a child site from a %s resource" % parent_site_type) def assign_device_to_site(self, device_id='', site_id=''): """Connects a device (any type) to a site (any subtype) @param device_id str @param site_id str @throws NotFound object with specified id does not exist """ site_obj = self.subsite.read_one(site_id) site_type = site_obj._get_type() if RT.PlatformSite == site_type: self.platform_site.link_device(site_id, device_id) elif RT.InstrumentSite == site_type: self.instrument_site.link_device(site_id, device_id) else: raise BadRequest("Tried to assign a device to a %s resource" % site_type) def unassign_device_from_site(self, device_id='', site_id=''): """Disconnects a device (any type) from a site (any subtype) @param device_id str @param site_id str @throws NotFound object with specified id does not exist """ site_obj = self.subsite.read_one(site_id) site_type = site_obj._get_type() if RT.PlatformSite == site_type: self.platform_site.unlink_device(site_id, device_id) elif RT.InstrumentSite == site_type: self.instrument_site.unlink_device(site_id, device_id) else: raise BadRequest("Tried to unassign a device from a %s resource" % site_type) def assign_site_to_observatory(self, site_id='', observatory_id=''): self.observatory.link_site(observatory_id, site_id) def unassign_site_from_observatory(self, site_id="", observatory_id=''): self.observatory.unlink_site(observatory_id, site_id) def assign_instrument_model_to_instrument_site(self, instrument_model_id='', instrument_site_id=''): self.instrument_site.link_model(instrument_site_id, instrument_model_id) def unassign_instrument_model_from_instrument_site(self, instrument_model_id='', instrument_site_id=''): self.instrument_site.unlink_model(instrument_site_id, instrument_model_id) def assign_platform_model_to_platform_site(self, platform_model_id='', platform_site_id=''): self.platform_site.link_model(platform_site_id, platform_model_id) def unassign_platform_model_from_platform_site(self, platform_model_id='', platform_site_id=''): self.platform_site.unlink_model(platform_site_id, platform_model_id) def assign_resource_to_observatory_org(self, resource_id='', org_id=''): if not org_id: raise BadRequest("Org id not given") if not resource_id: raise BadRequest("Resource id not given") log.debug("assign_resource_to_observatory_org: org_id=%s, resource_id=%s " % (org_id, resource_id)) self.clients.org_management.share_resource(org_id, resource_id) def unassign_resource_from_observatory_org(self, resource_id='', org_id=''): if not org_id: raise BadRequest("Org id not given") if not resource_id: raise BadRequest("Resource id not given") self.clients.org_management.unshare_resource(org_id, resource_id) ########################################################################## # # DEPLOYMENTS # ########################################################################## def deploy_instrument_site(self, instrument_site_id='', deployment_id=''): self.instrument_site.link_deployment(instrument_site_id, deployment_id) def undeploy_instrument_site(self, instrument_site_id='', deployment_id=''): self.instrument_site.unlink_deployment(instrument_site_id, deployment_id) def deploy_platform_site(self, platform_site_id='', deployment_id=''): self.platform_site.link_deployment(platform_site_id, deployment_id) def undeploy_platform_site(self, platform_site_id='', deployment_id=''): self.platform_site.unlink_deployment(platform_site_id, deployment_id) def create_site_data_product(self, site_id="", data_product_id=""): # verify that both exist site_obj = self.RR.read(site_id) data_product_obj = self.RR.read(data_product_id) sitetype = type(site_obj).__name__ if not (RT.InstrumentSite == sitetype or RT.PlatformSite == sitetype): raise BadRequest("Can't associate a data product to a %s" % sitetype) # validation prods, _ = self.RR.find_objects(site_id, PRED.hasOutputProduct, RT.DataProduct) if 0 < len(prods): raise BadRequest("%s '%s' already has an ouptut data product" % (sitetype, site_id)) sites, _ = self.RR.find_subjects(sitetype, PRED.hasOutputProduct, data_product_id) if 0 < len(sites): raise BadRequest("DataProduct '%s' is already an output product of a %s" % (data_product_id, sitetype)) #todo: re-use existing defintion? how? log.info("Creating data process definition") dpd_obj = IonObject(RT.DataProcessDefinition, name='logical_transform', description='send the packet from the in stream to the out stream unchanged', module='ion.processes.data.transforms.logical_transform', class_name='logical_transform', process_source="For %s '%s'" % (sitetype, site_id)) logical_transform_dprocdef_id = self.PRMS.create_data_process_definition(dpd_obj) log.info("Creating data process") dproc_id = self.PRMS.create_data_process(logical_transform_dprocdef_id, [], {"output":data_product_id}) log.info("Created data process") log.info("associating site hasOutputProduct") #make it all happen if RT.InstrumentSite == sitetype: self.instrument_site.link_output_product(site_id, data_product_id) elif RT.PlatformSite == sitetype: self.platform_site.link_output_product(site_id, data_product_id) def streamdef_of_site(self, site_id): """ return the streamdef associated with the output product of a site """ #assume we've previously validated that the site has 1 product p, _ = self.RR.find_objects(site_id, PRED.hasOutputProduct, RT.DataProduct, True) streams, _ = self.RR.find_objects(p[0], PRED.hasStream, RT.Stream, True) if 1 != len(streams): raise BadRequest("Expected 1 stream on DataProduct '%s', got %d" % (p[0], len(streams))) sdefs, _ = self.RR.find_objects(streams[0], PRED.hasStreamDefinition, RT.StreamDefinition, True) if 1 != len(sdefs): raise BadRequest("Expected 1 streamdef on StreamDefinition '%s', got %d" % (streams[0], len(sdefs))) return sdefs[0] def streamdefs_of_device(self, device_id): """ return a dict of streamdef_id => stream_id for a given device """ #recursive function to get all data producers def child_data_producers(dpdc_ids): def cdp_helper(acc2, dpdc_id2): children, _ = self.RR.find_subjects(RT.DataProducer, PRED.hasParent, dpdc_id2, True) for child in children: acc2.append(child) acc2 = cdp_helper(acc2, child) return acc #call helper using input list of data products acc = [] for d in dpdc_ids: acc = cdp_helper(acc, d) return acc #initial list of data producers pdcs, _ = self.RR.find_objects(device_id, PRED.hasDataProducer, RT.DataProducer, True) if 0 == len(pdcs): raise BadRequest("Expected data producer(s) on device '%s', got none" % device_id) #now the full list of data producers, with children pdcs = child_data_producers(pdcs) log.debug("Got %d data producers" % len(pdcs)) streamdefs = {} for pdc in pdcs: log.debug("Checking data prodcer %s" % pdc) prods, _ = self.RR.find_subjects(RT.DataProduct, PRED.hasDataProducer, pdc, True) for p in prods: log.debug("Checking product %s" % p) streams, _ = self.RR.find_objects(p, PRED.hasStream, RT.Stream, True) for s in streams: log.debug("Checking stream %s" % s) sdefs, _ = self.RR.find_objects(s, PRED.hasStreamDefinition, RT.StreamDefinition, True) for sd in sdefs: log.debug("Checking streamdef %s" % sd) if sd in streamdefs: raise BadRequest("Got a duplicate stream definition stemming from device %s" % device_id) streamdefs[sd] = s return streamdefs def check_site_for_deployment(self, site_id, site_type, model_type): log.debug("checking %s for deployment, will return %s" % (site_type, model_type)) # validate and return supported models models, _ = self.RR.find_objects(site_id, PRED.hasModel, model_type, True) if 1 > len(models): raise BadRequest("Expected at least 1 model for %s '%s', got %d" % (site_type, site_id, len(models))) log.debug("checking site data products") prods, _ = self.RR.find_objects(site_id, PRED.hasOutputProduct, RT.DataProduct, True) if 1 != len(prods): raise BadRequest("Expected 1 output data product on %s '%s', got %d" % (site_type, site_id, len(prods))) return models def check_device_for_deployment(self, device_id, device_type, model_type): log.debug("checking %s for deployment, will return %s" % (device_type, model_type)) # validate and return model models, _ = self.RR.find_objects(device_id, PRED.hasModel, model_type, True) if 1 != len(models): raise BadRequest("Expected 1 model for %s '%s', got %d" % (device_type, device_id, len(models))) return models[0] def check_site_device_pair_for_deployment(self, site_id, device_id, site_type=None, device_type=None): log.debug("checking %s/%s pair for deployment" % (site_type, device_type)) #return a pair that should be REMOVED, or None if site_type is None: site_type = type(self.RR.read(site_id)).__name__ if device_type is None: device_type = type(self.RR.read(device_id)).__name__ ret = None log.debug("checking existing hasDevice links from site") devices, _ = self.RR.find_objects(site_id, PRED.hasDevice, device_type, True) if 1 < len(devices): raise Inconsistent("Found more than 1 hasDevice relationship from %s '%s'" % (site_type, site_id)) elif 0 < len(devices): if devices[0] != device_id: ret = (site_id, devices[0]) log.info("%s '%s' is already hasDevice with a %s" % (site_type, site_id, device_type)) return ret def has_matching_streamdef(self, site_id, device_id): if not self.streamdef_of_site(site_id) in self.streamdefs_of_device(device_id): raise BadRequest("No matching streamdefs between %s '%s' and %s '%s'" % (site_type, site_id, device_type, device_id)) def collect_deployment_components(self, deployment_id): """ get all devices and sites associated with this deployment and use their ID as a key to list of models """ device_models = {} site_models = {} # significant change-up in how this works. # # collect all devices in this deployment def add_sites(site_ids, site_type, model_type): for s in site_ids: models = self.check_site_for_deployment(s, site_type, model_type) if s in site_models: log.warn("Site '%s' was already collected in deployment '%s'" % (s, deployment_id)) site_models[s] = models def add_devices(device_ids, device_type, model_type): for d in device_ids: model = self.check_device_for_deployment(d, device_type, model_type) if d in device_models: log.warn("Device '%s' was already collected in deployment '%s'" % (d, deployment_id)) device_models[d] = model def collect_specific_resources(site_type, device_type, model_type): # check this deployment -- specific device types -- for validity # return a list of pairs (site, device) to be associated new_site_ids, _ = self.RR.find_subjects(site_type, PRED.hasDeployment, deployment_id, True) new_device_ids, _ = self.RR.find_subjects(device_type, PRED.hasDeployment, deployment_id, True) add_sites(new_site_ids, site_type, model_type) add_devices(new_device_ids, site_type, model_type) # collect platforms, verify that only one platform device exists in the deployment collect_specific_resources(RT.PlatformSite, RT.PlatformDevice, RT.PlatformModel) if 1 < len(device_models): raise BadRequest("Multiple platforms in the same deployment are not allowed") elif 0 < len(device_models): # add devices and sites that are children of platform device / site child_device_ids = self.platform_device.find_stemming_device(device_models.keys()[0]) child_site_ids = self.find_related_frames_of_reference(site_models.keys()[0], [RT.PlatformSite, RT.InstrumentSite]) # IGNORE child platforms # verify that platform site has no sub-platform-sites #if 0 < len(child_site_ids[RT.PlatformSite]): # raise BadRequest("Deploying a platform with its own child platform is not allowed") # gather a list of all instrument sites on platform site # gather a list of all instrument devices on platform device add_devices(child_device_ids, RT.InstrumentDevice, RT.InstrumentModel) add_sites(child_site_ids[RT.InstrumentSite], RT.InstrumentSite, RT.InstrumentModel) collect_specific_resources(RT.InstrumentSite, RT.InstrumentDevice, RT.InstrumentModel) return device_models, site_models def activate_deployment(self, deployment_id='', activate_subscriptions=False): """ Make the devices on this deployment the primary devices for the sites """ #Verify that the deployment exists deployment_obj = self.clients.resource_registry.read(deployment_id) # if LCS.DEPLOYED == deployment_obj.lcstate: # raise BadRequest("This deploment is already active") device_models, site_models = self.collect_deployment_components(deployment_id) # create a CSP so we can solve it problem = constraint.Problem() # add variables - the devices to be assigned, and their range (possible sites) for device_id in device_models.keys(): device_model = device_models[device_id] possible_sites = [s for s in site_models.keys() if device_model in site_models[s]] #and self.streamdef_of_site(s) in self.streamdefs_of_device(device_id)] problem.addVariable("device_%s" % device_id, possible_sites) # add the constraint that all the variables have to pick their own site problem.addConstraint(constraint.AllDifferentConstraint(), ["device_%s" % device_id for device_id in device_models.keys()]) # perform CSP solve; this will be a list of solutions, each a dict of var -> value solutions = problem.getSolutions() if 1 > len(solutions): raise BadRequest("The set of devices could not be mapped to the set of sites, based on matching " + "model and streamdefs") elif 1 < len(solutions): log.warn("Found %d possible ways to map device and site, but just picking the first one" % len(solutions)) pairs_add = [] pairs_rem = [] for device_id in device_models.keys(): site_id = solutions[0]["device_%s" % device_id] old_pair = self.check_site_device_pair_for_deployment(site_id, device_id) if old_pair: pairs_rem.append(old_pair) new_pair = (site_id, device_id) pairs_add.append(new_pair) # process any removals for site_id, device_id in pairs_rem: log.info("Unassigning hasDevice; device '%s' from site '%s'" % (device_id, site_id)) if not activate_subscriptions: log.warn("The input to the data product for site '%s' will no longer come from its primary device" % site_id) self.unassign_device_from_site(device_id, site_id) # process the additions for site_id, device_id in pairs_add: log.info("Setting primary device '%s' for site '%s'" % (device_id, site_id)) self.assign_device_to_site(device_id, site_id) if activate_subscriptions: log.info("Activating subscription too") self.transfer_site_subscription(site_id) # # self.RR.execute_lifecycle_transition(deployment_id, LCE.DEPLOY) def deactivate_deployment(self, deployment_id=''): """Remove the primary device designation for the deployed devices at the sites @param deployment_id str @throws NotFound object with specified id does not exist @throws BadRequest if devices can not be undeployed """ #Verify that the deployment exists deployment_obj = self.clients.resource_registry.read(deployment_id) # if LCS.DEPLOYED != deployment_obj.lcstate: # raise BadRequest("This deploment is not active") # get all associated components device_models, site_models = self.collect_deployment_components(deployment_id) #must only remove from sites that are not deployed under a different active deployment # must only remove devices that are not deployed under a different active deployment def filter_alternate_deployments(resource_list): # return the list of ids for devices or sites not connected to an alternate lcs.deployed deployment ret = [] for r in resource_list: depls, _ = self.RR.find_objects(r, PRED.hasDeployment, RT.Deployment) keep = True for d in depls: if d._id != deployment_id and LCS.DEPLOYED == d.lcstate: keep = False if keep: ret.append(r) return ret device_ids = filter_alternate_deployments(device_models.keys()) site_ids = filter_alternate_deployments(site_models.keys()) # delete only associations where both site and device have passed the filter for s in site_ids: ds, _ = self.RR.find_objects(s, PRED.hasDevice, id_only=True) for d in ds: if d in device_ids: a = self.RR.get_association(s, PRED.hasDevice, d) self.RR.delete_association(a) # # # mark deployment as not deployed (developed seems appropriate) # self.RR.execute_lifecycle_transition(deployment_id, LCE.DEVELOPED) def transfer_site_subscription(self, site_id=""): """ Transfer the site subscription to the current hasDevice link """ # get site obj site_obj = self.RR.read(site_id) # error if no hasDevice devices, _ = self.RR.find_objects(site_id, PRED.hasDevice, None, True) if 1 != len(devices): raise BadRequest("Expected 1 hasDevice association, got %d" % len(devices)) device_id = devices[0] # get device obj device_obj = self.RR.read(device_id) # error if models don't match site_type = type(site_obj).__name__ device_type = type(device_obj).__name__ if RT.InstrumentDevice == device_type: model_type = RT.InstrumentModel elif RT.PlatformDevice == device_type: model_type = RT.PlatformModel else: raise BadRequest("Expected a device type, got '%s'" % device_type) device_model = self.check_device_for_deployment(device_id, device_type, model_type) site_models = self.check_site_for_deployment(site_id, site_type, model_type) if device_model not in site_models: raise BadRequest("The site and device model types are incompatible") # check site/device pair. # this function re-checks the association as a side effect, so this error should NEVER happen if self.check_site_device_pair_for_deployment(site_id, device_id, site_type, device_type): raise BadRequest("Magically and unfortunately, the site and device are no longer associated") # get deployments depl_site, _ = self.RR.find_objects(site_id, PRED.hasDeployment, RT.Deployment, True) depl_dev, _ = self.RR.find_objects(device_id, PRED.hasDeployment, RT.Deployment, True) # error if no matching deployments found = False for ds in depl_site: if ds in depl_dev: found = True break if not found: raise BadRequest("Site and device do not share a deployment!") # check product and process from site pduct_ids, _ = self.RR.find_objects(site_id, PRED.hasOutputProduct, RT.DataProduct, True) if 1 != len(pduct_ids): raise BadRequest("Expected 1 DataProduct associated to site '%s' but found %d" % (site_id, len(pduct_ids))) process_ids, _ = self.RR.find_subjects(RT.DataProcess, PRED.hasOutputProduct, pduct_ids[0], True) if 1 != len(process_ids): raise BadRequest("Expected 1 DataProcess feeding DataProduct '%s', but found %d" % (pduct_ids[0], len(process_ids))) #look up stream defs ss = self.streamdef_of_site(site_id) ds = self.streamdefs_of_device(device_id) if not ss in ds: raise BadRequest("Data product(s) of site does not have any matching streamdef for data product of device") data_process_id = process_ids[0] log.info("Changing subscription") self.PRMS.update_data_process_inputs(data_process_id, [ds[ss]]) ########################################################################## # # FIND OPS # ########################################################################## def find_org_by_observatory(self, observatory_id=''): """ """ orgs,_ = self.RR.find_subjects(RT.Org, PRED.hasResource, observatory_id, id_only=False) return orgs def find_related_frames_of_reference(self, input_resource_id='', output_resource_type_list=None): # the relative depth of each resource type in our tree depth = self.HIERARCHY_DEPTH input_obj = self.RR.read(input_resource_id) input_type = input_obj._get_type() #input type checking if not input_type in depth: raise BadRequest("Input resource type (got %s) must be one of %s" % (input_type, self.HIERARCHY_LOOKUP)) for t in output_resource_type_list: if not t in depth: raise BadRequest("Output resource types (got %s) must be one of %s" % (str(output_resource_type_list), self.HIERARCHY_LOOKUP)) subordinates = [x for x in output_resource_type_list if depth[x] > depth[input_type]] superiors = [x for x in output_resource_type_list if depth[x] < depth[input_type]] acc = {} acc[input_type] = [input_obj] if subordinates: # figure out the actual depth we need to go deepest_type = input_type #initial value for output_type in output_resource_type_list: if depth[deepest_type] < depth[output_type]: deepest_type = output_type log.debug("Deepest level for search will be '%s'" % deepest_type) acc = self._traverse_entity_tree(acc, input_type, deepest_type, True) if superiors: highest_type = input_type #initial value for output_type in output_resource_type_list: if depth[highest_type] > depth[output_type]: highest_type = output_type log.debug("Highest level for search will be '%s'" % highest_type) acc = self._traverse_entity_tree(acc, highest_type, input_type, False) # Don't include input type in response #TODO: maybe just remove the input resource id if input_type in acc: stripped = [] for r_obj in acc[input_type]: if r_obj._id == input_resource_id: log.debug("Stripped input from return value") else: stripped.append(r_obj) acc[input_type] = stripped return acc def _traverse_entity_tree(self, acc, top_type, bottom_type, downward): call_list = self._build_call_list(top_type, bottom_type, downward) # start calling functions if downward: for (p, c) in call_list: acc = self._find_subordinate(acc, p, c) else: for (p, c) in call_list: acc = self._find_superior(acc, p, c) return acc def _build_call_list(self, top_type, bottom_type, downward): call_list = [] if downward: step = 1 top_ord = self.HIERARCHY_DEPTH[top_type] bot_ord = self.HIERARCHY_DEPTH[bottom_type] else: step = -1 top_ord = self.HIERARCHY_DEPTH[bottom_type] - 1 bot_ord = self.HIERARCHY_DEPTH[top_type] - 1 for i in range(top_ord, bot_ord, step): child = self.HIERARCHY_LOOKUP[i + 1] parent = self.HIERARCHY_LOOKUP[i] if downward: tmp = [(parent, parent), (parent, child), (child, child)] else: tmp = [(child, child), (parent, child), (parent, parent)] for pair in tmp: if not pair in call_list: #log.debug("adding %s" % str(pair)) call_list.append(pair) return call_list def _find_subordinate(self, acc, parent_type, child_type): #acc is an accumulated dictionary find_fns = { (RT.Observatory, RT.Subsite): self.observatory.find_stemming_site, (RT.Subsite, RT.Subsite): self.subsite.find_stemming_subsite, (RT.Subsite, RT.PlatformSite): self.subsite.find_stemming_platform_site, (RT.PlatformSite, RT.PlatformSite): self.platform_site.find_stemming_platform_site, (RT.PlatformSite, RT.InstrumentSite): self.platform_site.find_stemming_instrument_site, } if (parent_type, child_type) in find_fns: find_fn = find_fns[(parent_type, child_type)] else: find_fn = (lambda x : []) if not parent_type in acc: acc[parent_type] = [] log.debug("Subordinates: '%s'x%d->'%s'" % (parent_type, len(acc[parent_type]), child_type)) #for all parents in the acc, add all their children for parent_obj in acc[parent_type]: parent_id = parent_obj._id for child_obj in find_fn(parent_id): actual_child_type = child_obj._get_type() if not actual_child_type in acc: acc[actual_child_type] = [] acc[actual_child_type].append(child_obj) return acc def _find_superior(self, acc, parent_type, child_type): # acc is an accumualted dictionary #log.debug("Superiors: '%s'->'%s'" % (parent_type, child_type)) #if True: # return acc find_fns = { (RT.Observatory, RT.Subsite): self.observatory.find_having_site, (RT.Subsite, RT.Subsite): self.subsite.find_having_site, (RT.Subsite, RT.PlatformSite): self.subsite.find_having_site, (RT.PlatformSite, RT.PlatformSite): self.platform_site.find_having_site, (RT.PlatformSite, RT.InstrumentSite): self.platform_site.find_having_site, } if (parent_type, child_type) in find_fns: find_fn = find_fns[(parent_type, child_type)] else: find_fn = (lambda x : []) if not child_type in acc: acc[child_type] = [] log.debug("Superiors: '%s'->'%s'x%d" % (parent_type, child_type, len(acc[child_type]))) #for all children in the acc, add all their parents for child_obj in acc[child_type]: child_id = child_obj._id for parent_obj in find_fn(child_id): actual_parent_type = parent_obj._get_type() if not actual_parent_type in acc: acc[actual_parent_type] = [] acc[actual_parent_type].append(parent_obj) return acc ############################ # # EXTENDED RESOURCES # ############################ def get_observatory_extension(self, observatory_id='', ext_associations=None, ext_exclude=None): """Returns an InstrumentDeviceExtension object containing additional related information @param observatory_id str @param ext_associations dict @param ext_exclude list @retval observatory ObservatoryExtension @throws BadRequest A parameter is missing @throws NotFound An object with the specified observatory_id does not exist """ if not observatory_id: raise BadRequest("The observatory_id parameter is empty") extended_resource_handler = ExtendedResourceContainer(self) extended_instrument = extended_resource_handler.create_extended_resource_container( OT.ObservatoryExtension, observatory_id, OT.ObservatoryComputedAttributes, ext_associations, ext_exclude) #Loop through any attachments and remove the actual content since we don't need to send it to the front end this way #TODO - see if there is a better way to do this in the extended resource frame work. if hasattr(extended_instrument, 'attachments'): for att in extended_instrument.attachments: if hasattr(att, 'content'): delattr(att, 'content') return extended_instrument #Bogus functions for computed attributes def get_number_data_sets(self, observatory_id): return "1" def get_number_instruments_deployed(self, observatory_id): return "1" def get_number_instruments_operational(self, observatory_id): return "1" def get_number_instruments_inoperational(self, observatory_id): return "1" def get_number_instruments(self, observatory_id): return "1" def get_number_platforms(self, observatory_id): return "1" def get_number_platforms_deployed(self, observatory_id): return "1"
class ObservatoryManagementService(BaseObservatoryManagementService): def on_init(self): IonObject("Resource") # suppress pyflakes error CFG, log, RT, PRED, LCS, LCE, NotFound, BadRequest, log #suppress pyflakes errors about "unused import" self.override_clients(self.clients) self.HIERARCHY_DEPTH = { RT.InstrumentSite: 3, RT.PlatformSite: 2, RT.Subsite: 1, RT.Observatory: 0, } self.HIERARCHY_LOOKUP = [ RT.Observatory, RT.Subsite, RT.PlatformSite, RT.InstrumentSite ] def override_clients(self, new_clients): """ Replaces the service clients with a new set of them... and makes sure they go to the right places """ #shortcut names for the import sub-services if hasattr(self.clients, "resource_registry"): self.RR = self.clients.resource_registry if hasattr(self.clients, "instrument_management"): self.IMS = self.clients.instrument_management #farm everything out to the impls self.org = OrgImpl(self.clients) self.observatory = ObservatoryImpl(self.clients) self.subsite = SubsiteImpl(self.clients) self.platform_site = PlatformSiteImpl(self.clients) self.instrument_site = InstrumentSiteImpl(self.clients) self.instrument_device = InstrumentDeviceImpl(self.clients) self.platform_device = PlatformDeviceImpl(self.clients) ########################################################################## # # CRUD OPS # ########################################################################## def create_marine_facility(self, org=None): """Create an Org (domain of authority) that realizes a marine facility. This Org will have set up roles for a marine facility. Shared resources, such as a device can only be registered in one marine facility Org, and additionally in many virtual observatory Orgs. The marine facility operators will have more extensive permissions and will supercede virtual observatory commands @param org Org @retval org_id str @throws BadRequest if object does not have _id or _rev attribute @throws NotFound object with specified id does not exist """ log.debug("ObservatoryManagementService.create_marine_facility(): %s" % org) # create the org org.org_type = OrgTypeEnum.MARINE_FACILITY org_id = self.clients.org_management.create_org(org) #Instantiate initial set of User Roles for this marine facility instrument_operator_role = IonObject( RT.UserRole, name=INSTRUMENT_OPERATOR_ROLE, label='Instrument Operator', description='Marine Facility Instrument Operator') self.clients.org_management.add_user_role(org_id, instrument_operator_role) observatory_operator_role = IonObject( RT.UserRole, name=OBSERVATORY_OPERATOR_ROLE, label='Observatory Operator', description='Marine Facility Observatory Operator') self.clients.org_management.add_user_role(org_id, observatory_operator_role) data_operator_role = IonObject( RT.UserRole, name=DATA_OPERATOR_ROLE, label='Data Operator', description='Marine Facility Data Operator') self.clients.org_management.add_user_role(org_id, data_operator_role) return org_id def create_virtual_observatory(self, org=None): """Create an Org (domain of authority) that realizes a virtual observatory. This Org will have set up roles for a virtual observatory. Shared resources, such as a device can only be registered in one marine facility Org, and additionally in many virtual observatory Orgs. The marine facility operators will have more extensive permissions and will supercede virtual observatory commands @param org Org @retval org_id str @throws BadRequest if object does not have _id or _rev attribute @throws NotFound object with specified id does not exist """ log.debug( "ObservatoryManagementService.create_virtual_observatory(): %s" % org) # create the org org.org_type = OrgTypeEnum.VIRTUAL_OBSERVATORY org_id = self.clients.org_management.create_org(org) return org_id def create_observatory(self, observatory=None): """Create a Observatory resource. An observatory is coupled with one Org. The Org is created and associated as part of this call. @param observatory Observatory @retval observatory_id str @throws BadRequest if object does not have _id or _rev attribute @throws NotFound object with specified id does not exist """ # create the marine facility observatory_id = self.observatory.create_one(observatory) return observatory_id def read_observatory(self, observatory_id=''): """Read a Observatory resource @param observatory_id str @retval observatory Observatory @throws NotFound object with specified id does not exist """ return self.observatory.read_one(observatory_id) def update_observatory(self, observatory=None): """Update a Observatory resource @param observatory Observatory @throws NotFound object with specified id does not exist """ return self.observatory.update_one(observatory) def delete_observatory(self, observatory_id=''): """Delete a Observatory resource @param observatory_id str @throws NotFound object with specified id does not exist """ # find the org for this MF org_ids, _ = self.clients.resource_registry.find_subjects( RT.Org, PRED.hasObservatory, observatory_id, id_only=True) if len(org_ids) == 0: log.warn( "ObservatoryManagementService.delete_observatory(): no org for MF " + observatory_id) else: if len(org_ids) > 1: log.warn( "ObservatoryManagementService.delete_observatory(): more than 1 org for MF " + observatory_id) # TODO: delete the others and/or raise exception??? # delete the set of User Roles for this marine facility that this service created self.clients.org_management.remove_user_role( org_ids[0], INSTRUMENT_OPERATOR_ROLE) self.clients.org_management.remove_user_role( org_ids[0], OBSERVATORY_OPERATOR_ROLE) self.clients.org_management.remove_user_role( org_ids[0], DATA_OPERATOR_ROLE) # delete the org self.clients.org_management.delete_org(org_ids[0]) return self.observatory.delete_one(observatory_id) def create_subsite(self, subsite=None, parent_id=''): """Create a Subsite resource. A subsite is a frame of reference within an observatory. Its parent is either the observatory or another subsite. @param subsite Subsite @param parent_id str @retval subsite_id str @throws BadRequest if object does not have _id or _rev attribute @throws NotFound object with specified id does not exist """ subsite_id = self.subsite.create_one(subsite) if parent_id: self.subsite.link_parent(subsite_id, parent_id) return subsite_id def read_subsite(self, subsite_id=''): """Read a Subsite resource @param subsite_id str @retval subsite Subsite @throws NotFound object with specified id does not exist """ return self.subsite.read_one(subsite_id) def update_subsite(self, subsite=None): """Update a Subsite resource @param subsite Subsite @throws NotFound object with specified id does not exist """ return self.subsite.update_one(subsite) def delete_subsite(self, subsite_id=''): """Delete a subsite resource, removes assocations to parents @param subsite_id str @throws NotFound object with specified id does not exist """ self.subsite.delete_one(subsite_id) def create_platform_site(self, platform_site=None, parent_id=''): """Create a PlatformSite resource. A platform_site is a frame of reference within an observatory. Its parent is either the observatory or another platform_site. @param platform_site PlatformSite @param parent_id str @retval platform_site_id str @throws BadRequest if object does not have _id or _rev attribute @throws NotFound object with specified id does not exist """ platform_site_id = self.platform_site.create_one(platform_site) if parent_id: self.platform_site.link_parent(platform_site_id, parent_id) return platform_site_id def read_platform_site(self, platform_site_id=''): """Read a PlatformSite resource @param platform_site_id str @retval platform_site PlatformSite @throws NotFound object with specified id does not exist """ return self.platform_site.read_one(platform_site_id) def update_platform_site(self, platform_site=None): """Update a PlatformSite resource @param platform_site PlatformSite @throws NotFound object with specified id does not exist """ return self.platform_site.update_one(platform_site) def delete_platform_site(self, platform_site_id=''): """Delete a PlatformSite resource, removes assocations to parents @param platform_site_id str @throws NotFound object with specified id does not exist """ self.platform_site.delete_one(platform_site_id) def create_instrument_site(self, instrument_site=None, parent_id=''): """Create a InstrumentSite resource. A instrument_site is a frame of reference within an observatory. Its parent is either the observatory or another instrument_site. @param instrument_site InstrumentSite @param parent_id str @retval instrument_site_id str @throws BadRequest if object does not have _id or _rev attribute @throws NotFound object with specified id does not exist """ instrument_site_id = self.instrument_site.create_one(instrument_site) if parent_id: self.instrument_site.link_parent(instrument_site_id, parent_id) return instrument_site_id def read_instrument_site(self, instrument_site_id=''): """Read a InstrumentSite resource @param instrument_site_id str @retval instrument_site InstrumentSite @throws NotFound object with specified id does not exist """ return self.instrument_site.read_one(instrument_site_id) def update_instrument_site(self, instrument_site=None): """Update a InstrumentSite resource @param instrument_site InstrumentSite @throws NotFound object with specified id does not exist """ return self.instrument_site.update_one(instrument_site) def delete_instrument_site(self, instrument_site_id=''): """Delete a InstrumentSite resource, removes assocations to parents @param instrument_site_id str @throws NotFound object with specified id does not exist """ self.instrument_site.delete_one(instrument_site_id) def create_deployment(self, deployment=None, site_id='', device_id=''): """ Create a Deployment resource. Represents a (possibly open-ended) time interval grouping one or more resources within a given context, such as an instrument deployment on a platform at an observatory site. """ #Verify that site and device exist site_obj = self.clients.resource_registry.read(site_id) if not site_obj: raise NotFound("Deployment site %s does not exist" % site_id) device_obj = self.clients.resource_registry.read(device_id) if not device_obj: raise NotFound("Deployment device %s does not exist" % device_id) deployment_id, version = self.clients.resource_registry.create( deployment) # Create the links self.clients.resource_registry.create_association( site_id, PRED.hasDeployment, deployment_id) self.clients.resource_registry.create_association( device_id, PRED.hasDeployment, deployment_id) return deployment_id def update_deployment(self, deployment=None): # Overwrite Deployment object self.clients.resource_registry.update(deployment) def read_deployment(self, deployment_id=''): # Read Deployment object with _id matching id log.debug("Reading Deployment object id: %s" % deployment_id) deployment_obj = self.clients.resource_registry.read(deployment_id) return deployment_obj def delete_deployment(self, deployment_id=''): """ Delete a Deployment resource """ #Verify that the deployment exist deployment_obj = self.clients.resource_registry.read(deployment_id) if not deployment_obj: raise NotFound("Deployment %s does not exist" % deployment_id) # Remove the link between the Stream Definition resource and the Data Process Definition resource associations = self.clients.resource_registry.find_associations( None, PRED.hasDeployment, deployment_id, id_only=True) if not associations: raise NotFound( "No Sites or Devices associated with this Deployment identifier " + str(deployment_id)) for association in associations: self.clients.resource_registry.delete_association(association) # Delete the deployment self.clients.resource_registry.delete(deployment_id) def activate_deployment(self, deployment_id=''): """ Make the devices on this deployment the primary devices for the sites """ #todo: FOR NOW the deployment must have one platform site and one platform device #Verify that the deployment exist deployment_obj = self.clients.resource_registry.read(deployment_id) if not deployment_obj: raise NotFound("Deployment %s does not exist", str(deployment_id)) # get the device and site attached to this deployment #todo: generalize this to handle multi devices? How to pair the Sites and Devices attached? site_ids, _ = self.clients.resource_registry.find_subjects( RT.PlatformSite, PRED.hasDeployment, deployment_id, True) if len(site_ids) < 1: raise NotFound("Deployment %s does not have associated Sites", str(deployment_id)) else: site_id = site_ids[0] device_ids, _ = self.clients.resource_registry.find_subjects( RT.PlatformDevice, PRED.hasDeployment, deployment_id, True) if len(device_ids) < 1: raise NotFound("Deployment %s does not have associated Devices", str(deployment_id)) else: device_id = device_ids[0] #Check that the models match at the Platform level device_models, _ = self.clients.resource_registry.find_objects( device_id, PRED.hasModel, RT.PlatformModel, True) if len(device_models) != 1: raise BadRequest( "Platform Device %s has multiple models associated %s", str(device_id), str(len(device_models))) site_models, _ = self.clients.resource_registry.find_objects( site_id, PRED.hasModel, RT.PlatformModel, True) if len(site_models) != 1: raise BadRequest( "Platform Site %s has multiple models associated %s", str(device_id), str(len(device_models))) if device_models[0] != site_models[0]: raise BadRequest( "Platform Site Model %s does not match Platform Device Model %s", str(site_models[0]), str(device_models[0])) #Check that the Site does not already have an associated primary device prim_device_ids, _ = self.clients.resource_registry.find_objects( site_id, PRED.hasDevice, RT.Device, True) if len(prim_device_ids) > 0: raise BadRequest( "Site %s already has a primary device associated with id %s", str(site_id), str(prim_device_ids[0])) else: self.deploy_device_to_site(device_id, site_id) log.debug( "ObsMS:activate_deployment plaform device: %s deployed to platform site: %s", str(device_id), str(site_id)) #retrieve the assoc instrument devices on this platform device inst_device_ids, _ = self.clients.resource_registry.find_objects( device_id, PRED.hasDevice, RT.InstrumentDevice, True) #retrieve the assoc instrument sites on this platform site inst_site_ids, _ = self.clients.resource_registry.find_objects( site_id, PRED.hasSite, RT.InstrumentSite, True) #pair the instrument devices to instrument sites if they have equivalent models for inst_device_id in inst_device_ids: log.debug( "ObsMS:activate_deployment find match for instrument device: %s", str(inst_device_id)) #get the model of this inst device, should be exactly one model attached inst_device_models, _ = self.clients.resource_registry.find_objects( inst_device_id, PRED.hasModel, RT.InstrumentModel, True) if len(inst_device_models) != 1: raise BadRequest( "Instrument Device %s has multiple models associated %s", str(inst_device_id), str(len(inst_device_models))) log.debug("ObsMS:activate_deployment inst_device_model: %s", str(inst_device_models[0])) for inst_site_id in inst_site_ids: #get the model of this inst site, should be exactly one model attached inst_site_models, _ = self.clients.resource_registry.find_objects( inst_site_id, PRED.hasModel, RT.InstrumentModel, True) if len(inst_device_models) != 1: raise BadRequest( "Instrument Site %s has multiple models associated: %s", str(inst_device_id), str(len(inst_device_models))) else: log.debug("ObsMS:activate_deployment inst_site_model: %s", str(inst_site_models[0])) #if the models match then deply this device into this site and remove this site from the list if inst_site_models[0] == inst_device_models[0]: self.deploy_device_to_site(inst_device_id, inst_site_id) log.debug( "ObsMS:activate_deployment match found for instrument device: %s and site: %s", str(inst_device_id), str(inst_site_id)) #remove this site from the list of available instrument sites on the platform inst_site_ids.remove(inst_site_id) log.debug( "ObsMS:activate_deployment instrument site list size: %s ", str(inst_site_ids)) break # go to the next inst_device on this platform and try to find a match #todo: throw an error if no match found? log.debug( "ObsMS:activate_deployment No matching site found for instrument device: %s", str(inst_device_id)) return def deploy_device_to_site(self, device_id='', site_id=''): """ link a device to a site as the primary instrument """ #Check that the Site does not already have an associated primary device prim_device_ids, _ = self.clients.resource_registry.find_objects( site_id, PRED.hasDevice, RT.InstrumentDevice, True) if len(prim_device_ids) != 0: raise BadRequest( "Site %s already has a primary device associated with id %s", str(site_id), str(prim_device_ids[0])) else: # Create the links self.clients.resource_registry.create_association( site_id, PRED.hasDevice, device_id) return def undeploy_device_from_site(self, device_id='', site_id=''): """ remove the link between a device and site which designates the instrument as primary """ #Check that the Site and Device are associated as primary device assoc_ids, _ = self.clients.resource_registry.find_associations( site_id, PRED.hasDevice, device_id, True) if len(assoc_ids) != 1: raise BadRequest( "Site %s does not have device %s associated as the primary device", str(site_id), str(device_id)) else: # Create the links self.clients.resource_registry.delete_association(assoc_ids[0]) return ############################ # # ASSOCIATIONS # ############################ def assign_site_to_site(self, child_site_id='', parent_site_id=''): """Connects a child site (any subtype) to a parent site (any subtype) @param child_site_id str @param parent_site_id str @throws NotFound object with specified id does not exist """ parent_site_obj = self.subsite.read_one(parent_site_id) parent_site_type = parent_site_obj._get_type() if RT.Observatory == parent_site_type: self.observatory.link_site(parent_site_id, child_site_id) elif RT.Subsite == parent_site_type: self.subsite.link_site(parent_site_id, child_site_id) elif RT.PlatformSite == parent_site_type: self.platform_site.link_site(parent_site_id, child_site_id) else: raise BadRequest("Tried to assign a child site to a %s resource" % parent_site_type) def unassign_site_from_site(self, child_site_id='', parent_site_id=''): """Disconnects a child site (any subtype) from a parent site (any subtype) @param child_site_id str @param parent_site_id str @throws NotFound object with specified id does not exist """ parent_site_obj = self.subsite.read_one(parent_site_id) parent_site_type = parent_site_obj._get_type() if RT.Observatory == parent_site_type: self.observatory.unlink_site(parent_site_id, child_site_id) elif RT.Subsite == parent_site_type: self.subsite.unlink_site(parent_site_id, child_site_id) elif RT.PlatformSite == parent_site_type: self.platform_site.unlink_site(parent_site_id, child_site_id) else: raise BadRequest( "Tried to unassign a child site from a %s resource" % parent_site_type) def assign_device_to_site(self, device_id='', site_id=''): """Connects a device (any type) to a site (any subtype) @param device_id str @param site_id str @throws NotFound object with specified id does not exist """ site_obj = self.subsite.read_one(site_id) site_type = site_obj._get_type() if RT.PlatformSite == site_type: self.platform_site.link_device(site_id, device_id) elif RT.InstrumentSite == site_type: self.instrument_site.link_device(site_id, device_id) else: raise BadRequest("Tried to assign a device to a %s resource" % site_type) def unassign_device_from_site(self, device_id='', site_id=''): """Disconnects a device (any type) from a site (any subtype) @param device_id str @param site_id str @throws NotFound object with specified id does not exist """ site_obj = self.subsite.read_one(site_id) site_type = site_obj._get_type() if RT.PlatformSite == site_type: self.platform_site.unlink_device(site_id, device_id) elif RT.InstrumentSite == site_type: self.instrument_site.unlink_device(site_id, device_id) else: raise BadRequest("Tried to unassign a device from a %s resource" % site_type) def assign_site_to_observatory(self, site_id='', observatory_id=''): self.observatory.link_site(observatory_id, site_id) def unassign_site_from_observatory(self, site_id="", observatory_id=''): self.observatory.unlink_site(observatory_id, site_id) def assign_instrument_model_to_instrument_site(self, instrument_model_id='', instrument_site_id=''): self.instrument_site.link_model(instrument_site_id, instrument_model_id) def unassign_instrument_model_from_instrument_site(self, instrument_model_id='', instrument_site_id=''): self.instrument_site.unlink_model(instrument_site_id, instrument_model_id) def assign_platform_model_to_platform_site(self, platform_model_id='', platform_site_id=''): self.platform_site.link_model(platform_site_id, platform_model_id) def unassign_platform_model_from_platform_site(self, platform_model_id='', platform_site_id=''): self.platform_site.unlink_model(platform_site_id, platform_model_id) def assign_resource_to_observatory_org(self, resource_id='', org_id=''): if not org_id: raise BadRequest("Org id not given") if not resource_id: raise BadRequest("Resource id not given") log.debug( "assign_resource_to_observatory_org: org_id=%s, resource_id=%s " % (org_id, resource_id)) self.clients.org_management.share_resource(org_id, resource_id) def unassign_resource_from_observatory_org(self, resource_id='', org_id=''): if not org_id: raise BadRequest("Org id not given") if not resource_id: raise BadRequest("Resource id not given") self.clients.org_management.unshare_resource(org_id, resource_id) ########################################################################## # # DEPLOYMENTS # ########################################################################## def deploy_instrument_site(self, instrument_site_id='', deployment_id=''): self.instrument_site.link_deployment(instrument_site_id, deployment_id) def undeploy_instrument_site(self, instrument_site_id='', deployment_id=''): self.instrument_site.unlink_deployment(instrument_site_id, deployment_id) def deploy_platform_site(self, platform_site_id='', deployment_id=''): self.platform_site.link_deployment(platform_site_id, deployment_id) def undeploy_platform_site(self, platform_site_id='', deployment_id=''): self.platform_site.unlink_deployment(platform_site_id, deployment_id) ########################################################################## # # FIND OPS # ########################################################################## def find_org_by_observatory(self, observatory_id=''): """ """ orgs, _ = self.RR.find_subjects(RT.Org, PRED.hasResource, observatory_id, id_only=False) return orgs def find_related_frames_of_reference(self, input_resource_id='', output_resource_type_list=None): # the relative depth of each resource type in our tree depth = self.HIERARCHY_DEPTH input_obj = self.RR.read(input_resource_id) input_type = input_obj._get_type() #input type checking if not input_type in depth: raise BadRequest("Input resource type (got %s) must be one of %s" % (input_type, self.HIERARCHY_LOOKUP)) for t in output_resource_type_list: if not t in depth: raise BadRequest( "Output resource types (got %s) must be one of %s" % (str(output_resource_type_list), self.HIERARCHY_LOOKUP)) subordinates = [ x for x in output_resource_type_list if depth[x] > depth[input_type] ] superiors = [ x for x in output_resource_type_list if depth[x] < depth[input_type] ] acc = {} acc[input_type] = [input_obj] if subordinates: # figure out the actual depth we need to go deepest_type = input_type #initial value for output_type in output_resource_type_list: if depth[deepest_type] < depth[output_type]: deepest_type = output_type log.debug("Deepest level for search will be '%s'" % deepest_type) acc = self._traverse_entity_tree(acc, input_type, deepest_type, True) if superiors: highest_type = input_type #initial value for output_type in output_resource_type_list: if depth[highest_type] > depth[output_type]: highest_type = output_type log.debug("Highest level for search will be '%s'" % highest_type) acc = self._traverse_entity_tree(acc, highest_type, input_type, False) # Don't include input type in response #TODO: maybe just remove the input resource id if input_type in acc: stripped = [] for r_obj in acc[input_type]: if r_obj._id == input_resource_id: log.debug("Stripped input from return value") else: stripped.append(r_obj) acc[input_type] = stripped return acc def _traverse_entity_tree(self, acc, top_type, bottom_type, downward): call_list = self._build_call_list(top_type, bottom_type, downward) # start calling functions if downward: for (p, c) in call_list: acc = self._find_subordinate(acc, p, c) else: for (p, c) in call_list: acc = self._find_superior(acc, p, c) return acc def _build_call_list(self, top_type, bottom_type, downward): call_list = [] if downward: step = 1 top_ord = self.HIERARCHY_DEPTH[top_type] bot_ord = self.HIERARCHY_DEPTH[bottom_type] else: step = -1 top_ord = self.HIERARCHY_DEPTH[bottom_type] - 1 bot_ord = self.HIERARCHY_DEPTH[top_type] - 1 for i in range(top_ord, bot_ord, step): child = self.HIERARCHY_LOOKUP[i + 1] parent = self.HIERARCHY_LOOKUP[i] if downward: tmp = [(parent, parent), (parent, child), (child, child)] else: tmp = [(child, child), (parent, child), (parent, parent)] for pair in tmp: if not pair in call_list: #log.debug("adding %s" % str(pair)) call_list.append(pair) return call_list def _find_subordinate(self, acc, parent_type, child_type): #acc is an accumulated dictionary find_fns = { (RT.Observatory, RT.Subsite): self.observatory.find_stemming_site, (RT.Subsite, RT.Subsite): self.subsite.find_stemming_subsite, (RT.Subsite, RT.PlatformSite): self.subsite.find_stemming_platform_site, (RT.PlatformSite, RT.PlatformSite): self.platform_site.find_stemming_platform_site, (RT.PlatformSite, RT.InstrumentSite): self.platform_site.find_stemming_instrument_site, } if (parent_type, child_type) in find_fns: find_fn = find_fns[(parent_type, child_type)] else: find_fn = (lambda x: []) if not parent_type in acc: acc[parent_type] = [] log.debug("Subordinates: '%s'x%d->'%s'" % (parent_type, len(acc[parent_type]), child_type)) #for all parents in the acc, add all their children for parent_obj in acc[parent_type]: parent_id = parent_obj._id for child_obj in find_fn(parent_id): actual_child_type = child_obj._get_type() if not actual_child_type in acc: acc[actual_child_type] = [] acc[actual_child_type].append(child_obj) return acc def _find_superior(self, acc, parent_type, child_type): # acc is an accumualted dictionary #log.debug("Superiors: '%s'->'%s'" % (parent_type, child_type)) #if True: # return acc find_fns = { (RT.Observatory, RT.Subsite): self.observatory.find_having_site, (RT.Subsite, RT.Subsite): self.subsite.find_having_site, (RT.Subsite, RT.PlatformSite): self.subsite.find_having_site, (RT.PlatformSite, RT.PlatformSite): self.platform_site.find_having_site, (RT.PlatformSite, RT.InstrumentSite): self.platform_site.find_having_site, } if (parent_type, child_type) in find_fns: find_fn = find_fns[(parent_type, child_type)] else: find_fn = (lambda x: []) if not child_type in acc: acc[child_type] = [] log.debug("Superiors: '%s'->'%s'x%d" % (parent_type, child_type, len(acc[child_type]))) #for all children in the acc, add all their parents for child_obj in acc[child_type]: child_id = child_obj._id for parent_obj in find_fn(child_id): actual_parent_type = parent_obj._get_type() if not actual_parent_type in acc: acc[actual_parent_type] = [] acc[actual_parent_type].append(parent_obj) return acc
class ObservatoryManagementService(BaseObservatoryManagementService): def on_init(self): IonObject("Resource") # suppress pyflakes error CFG, log, RT, PRED, LCS, LCE, NotFound, BadRequest, log #suppress pyflakes errors about "unused import" self.override_clients(self.clients) self.HIERARCHY_DEPTH = {RT.InstrumentSite: 3, RT.PlatformSite: 2, RT.Subsite: 1, RT.Observatory: 0, } self.HIERARCHY_LOOKUP = [RT.Observatory, RT.Subsite, RT.PlatformSite, RT.InstrumentSite] def override_clients(self, new_clients): """ Replaces the service clients with a new set of them... and makes sure they go to the right places """ #shortcut names for the import sub-services if hasattr(self.clients, "resource_registry"): self.RR = self.clients.resource_registry if hasattr(self.clients, "instrument_management"): self.IMS = self.clients.instrument_management #farm everything out to the impls self.org = OrgImpl(self.clients) self.observatory = ObservatoryImpl(self.clients) self.subsite = SubsiteImpl(self.clients) self.platform_site = PlatformSiteImpl(self.clients) self.instrument_site = InstrumentSiteImpl(self.clients) self.instrument_device = InstrumentDeviceImpl(self.clients) self.platform_device = PlatformDeviceImpl(self.clients) ########################################################################## # # CRUD OPS # ########################################################################## def create_marine_facility(self, org=None): """Create an Org (domain of authority) that realizes a marine facility. This Org will have set up roles for a marine facility. Shared resources, such as a device can only be registered in one marine facility Org, and additionally in many virtual observatory Orgs. The marine facility operators will have more extensive permissions and will supercede virtual observatory commands @param org Org @retval org_id str @throws BadRequest if object does not have _id or _rev attribute @throws NotFound object with specified id does not exist """ log.debug("ObservatoryManagementService.create_marine_facility(): %s" % org) # create the org org.org_type = OrgTypeEnum.MARINE_FACILITY org_id = self.clients.org_management.create_org(org) #Instantiate initial set of User Roles for this marine facility instrument_operator_role = IonObject(RT.UserRole, name=INSTRUMENT_OPERATOR_ROLE, label='Instrument Operator', description='Marine Facility Instrument Operator') self.clients.org_management.add_user_role(org_id, instrument_operator_role) observatory_operator_role = IonObject(RT.UserRole, name=OBSERVATORY_OPERATOR_ROLE, label='Observatory Operator', description='Marine Facility Observatory Operator') self.clients.org_management.add_user_role(org_id, observatory_operator_role) data_operator_role = IonObject(RT.UserRole, name=DATA_OPERATOR_ROLE, label='Data Operator', description='Marine Facility Data Operator') self.clients.org_management.add_user_role(org_id, data_operator_role) return org_id def create_virtual_observatory(self, org=None): """Create an Org (domain of authority) that realizes a virtual observatory. This Org will have set up roles for a virtual observatory. Shared resources, such as a device can only be registered in one marine facility Org, and additionally in many virtual observatory Orgs. The marine facility operators will have more extensive permissions and will supercede virtual observatory commands @param org Org @retval org_id str @throws BadRequest if object does not have _id or _rev attribute @throws NotFound object with specified id does not exist """ log.debug("ObservatoryManagementService.create_virtual_observatory(): %s" % org) # create the org org.org_type = OrgTypeEnum.VIRTUAL_OBSERVATORY org_id = self.clients.org_management.create_org(org) return org_id def create_observatory(self, observatory=None): """Create a Observatory resource. An observatory is coupled with one Org. The Org is created and associated as part of this call. @param observatory Observatory @retval observatory_id str @throws BadRequest if object does not have _id or _rev attribute @throws NotFound object with specified id does not exist """ # create the marine facility observatory_id = self.observatory.create_one(observatory) return observatory_id def read_observatory(self, observatory_id=''): """Read a Observatory resource @param observatory_id str @retval observatory Observatory @throws NotFound object with specified id does not exist """ return self.observatory.read_one(observatory_id) def update_observatory(self, observatory=None): """Update a Observatory resource @param observatory Observatory @throws NotFound object with specified id does not exist """ return self.observatory.update_one(observatory) def delete_observatory(self, observatory_id=''): """Delete a Observatory resource @param observatory_id str @throws NotFound object with specified id does not exist """ # find the org for this MF org_ids, _ = self.clients.resource_registry.find_subjects(RT.Org, PRED.hasObservatory, observatory_id, id_only=True) if len(org_ids) == 0: log.warn("ObservatoryManagementService.delete_observatory(): no org for MF " + observatory_id) else: if len(org_ids) > 1: log.warn("ObservatoryManagementService.delete_observatory(): more than 1 org for MF " + observatory_id) # TODO: delete the others and/or raise exception??? # delete the set of User Roles for this marine facility that this service created self.clients.org_management.remove_user_role(org_ids[0], INSTRUMENT_OPERATOR_ROLE) self.clients.org_management.remove_user_role(org_ids[0], OBSERVATORY_OPERATOR_ROLE) self.clients.org_management.remove_user_role(org_ids[0], DATA_OPERATOR_ROLE) # delete the org self.clients.org_management.delete_org(org_ids[0]) #todo: self.observatory.advance_lcs(observatory_id, LCE.RETIRE) return self.observatory.delete_one(observatory_id) def create_subsite(self, subsite=None, parent_id=''): """Create a Subsite resource. A subsite is a frame of reference within an observatory. Its parent is either the observatory or another subsite. @param subsite Subsite @param parent_id str @retval subsite_id str @throws BadRequest if object does not have _id or _rev attribute @throws NotFound object with specified id does not exist """ subsite_id = self.subsite.create_one(subsite) if parent_id: self.subsite.link_parent(subsite_id, parent_id) return subsite_id def read_subsite(self, subsite_id=''): """Read a Subsite resource @param subsite_id str @retval subsite Subsite @throws NotFound object with specified id does not exist """ return self.subsite.read_one(subsite_id) def update_subsite(self, subsite=None): """Update a Subsite resource @param subsite Subsite @throws NotFound object with specified id does not exist """ return self.subsite.update_one(subsite) def delete_subsite(self, subsite_id=''): """Delete a subsite resource, removes assocations to parents @param subsite_id str @throws NotFound object with specified id does not exist """ # todo self.subsite.advance_lcs(subsite_id, LCE.RETIRE) self.subsite.delete_one(subsite_id) def create_platform_site(self, platform_site=None, parent_id=''): """Create a PlatformSite resource. A platform_site is a frame of reference within an observatory. Its parent is either the observatory or another platform_site. @param platform_site PlatformSite @param parent_id str @retval platform_site_id str @throws BadRequest if object does not have _id or _rev attribute @throws NotFound object with specified id does not exist """ platform_site_id = self.platform_site.create_one(platform_site) if parent_id: self.platform_site.link_parent(platform_site_id, parent_id) return platform_site_id def read_platform_site(self, platform_site_id=''): """Read a PlatformSite resource @param platform_site_id str @retval platform_site PlatformSite @throws NotFound object with specified id does not exist """ return self.platform_site.read_one(platform_site_id) def update_platform_site(self, platform_site=None): """Update a PlatformSite resource @param platform_site PlatformSite @throws NotFound object with specified id does not exist """ return self.platform_site.update_one(platform_site) def delete_platform_site(self, platform_site_id=''): """Delete a PlatformSite resource, removes assocations to parents @param platform_site_id str @throws NotFound object with specified id does not exist """ # todo self.platform_site.advance_lcs(platform_site_id, LCE.RETIRE) self.platform_site.delete_one(platform_site_id) def create_instrument_site(self, instrument_site=None, parent_id=''): """Create a InstrumentSite resource. A instrument_site is a frame of reference within an observatory. Its parent is either the observatory or another instrument_site. @param instrument_site InstrumentSite @param parent_id str @retval instrument_site_id str @throws BadRequest if object does not have _id or _rev attribute @throws NotFound object with specified id does not exist """ instrument_site_id = self.instrument_site.create_one(instrument_site) if parent_id: self.instrument_site.link_parent(instrument_site_id, parent_id) return instrument_site_id def read_instrument_site(self, instrument_site_id=''): """Read a InstrumentSite resource @param instrument_site_id str @retval instrument_site InstrumentSite @throws NotFound object with specified id does not exist """ return self.instrument_site.read_one(instrument_site_id) def update_instrument_site(self, instrument_site=None): """Update a InstrumentSite resource @param instrument_site InstrumentSite @throws NotFound object with specified id does not exist """ return self.instrument_site.update_one(instrument_site) def delete_instrument_site(self, instrument_site_id=''): """Delete a InstrumentSite resource, removes assocations to parents @param instrument_site_id str @throws NotFound object with specified id does not exist """ # todo: self.instrument_site.advance_lcs(instrument_site_id, LCE.RETIRE) self.instrument_site.delete_one(instrument_site_id) def create_deployment(self, deployment=None, site_id='', device_id=''): """ Create a Deployment resource. Represents a (possibly open-ended) time interval grouping one or more resources within a given context, such as an instrument deployment on a platform at an observatory site. """ #Verify that site and device exist site_obj = self.clients.resource_registry.read(site_id) if not site_obj: raise NotFound("Deployment site %s does not exist" % site_id) device_obj = self.clients.resource_registry.read(device_id) if not device_obj: raise NotFound("Deployment device %s does not exist" % device_id) deployment_id, version = self.clients.resource_registry.create(deployment) # Create the links self.clients.resource_registry.create_association(site_id, PRED.hasDeployment, deployment_id) self.clients.resource_registry.create_association(device_id, PRED.hasDeployment, deployment_id) return deployment_id def update_deployment(self, deployment=None): # Overwrite Deployment object self.clients.resource_registry.update(deployment) def read_deployment(self, deployment_id=''): # Read Deployment object with _id matching id log.debug("Reading Deployment object id: %s" % deployment_id) deployment_obj = self.clients.resource_registry.read(deployment_id) return deployment_obj def delete_deployment(self, deployment_id=''): """ Delete a Deployment resource """ #Verify that the deployment exist deployment_obj = self.clients.resource_registry.read(deployment_id) if not deployment_obj: raise NotFound("Deployment %s does not exist" % deployment_id) # Remove the link between the Stream Definition resource and the Data Process Definition resource associations = self.clients.resource_registry.find_associations(None, PRED.hasDeployment, deployment_id, id_only=True) if not associations: raise NotFound("No Sites or Devices associated with this Deployment identifier " + str(deployment_id)) for association in associations: self.clients.resource_registry.delete_association(association) # Delete the deployment self.clients.resource_registry.delete(deployment_id) def activate_deployment(self, deployment_id=''): """ Make the devices on this deployment the primary devices for the sites """ #todo: FOR NOW the deployment must have one platform site and one platform device #Verify that the deployment exist deployment_obj = self.clients.resource_registry.read(deployment_id) if not deployment_obj: raise NotFound("Deployment %s does not exist", str(deployment_id)) # get the device and site attached to this deployment #todo: generalize this to handle multi devices? How to pair the Sites and Devices attached? site_ids, _ = self.clients.resource_registry.find_subjects(RT.PlatformSite, PRED.hasDeployment, deployment_id, True) if len(site_ids) < 1: raise NotFound("Deployment %s does not have associated Sites", str(deployment_id)) else: site_id = site_ids[0] device_ids, _ = self.clients.resource_registry.find_subjects(RT.PlatformDevice, PRED.hasDeployment, deployment_id, True) if len(device_ids) < 1: raise NotFound("Deployment %s does not have associated Devices", str(deployment_id)) else: device_id = device_ids[0] #Check that the models match at the Platform level device_models, _ = self.clients.resource_registry.find_objects(device_id, PRED.hasModel, RT.PlatformModel, True) if len(device_models) != 1: raise BadRequest("Platform Device %s has multiple models associated %s", str(device_id), str(len(device_models))) site_models, _ = self.clients.resource_registry.find_objects(site_id, PRED.hasModel, RT.PlatformModel, True) if len(site_models) != 1: raise BadRequest("Platform Site %s has multiple models associated %s", str(device_id), str(len(device_models))) if device_models[0] != site_models[0]: raise BadRequest("Platform Site Model %s does not match Platform Device Model %s", str(site_models[0]), str(device_models[0]) ) #Check that the Site does not already have an associated primary device prim_device_ids, _ = self.clients.resource_registry.find_objects(site_id, PRED.hasDevice, RT.Device, True) if len(prim_device_ids) > 0: raise BadRequest("Site %s already has a primary device associated with id %s", str(site_id), str(prim_device_ids[0])) else: self.deploy_device_to_site(device_id, site_id) log.debug("ObsMS:activate_deployment plaform device: %s deployed to platform site: %s", str(device_id), str(site_id)) #retrieve the assoc instrument devices on this platform device inst_device_ids, _ = self.clients.resource_registry.find_objects(device_id, PRED.hasDevice, RT.InstrumentDevice, True) #retrieve the assoc instrument sites on this platform site inst_site_ids, _ = self.clients.resource_registry.find_objects(site_id, PRED.hasSite, RT.InstrumentSite, True) #pair the instrument devices to instrument sites if they have equivalent models for inst_device_id in inst_device_ids: log.debug("ObsMS:activate_deployment find match for instrument device: %s", str(inst_device_id)) #get the model of this inst device, should be exactly one model attached inst_device_models, _ = self.clients.resource_registry.find_objects(inst_device_id, PRED.hasModel, RT.InstrumentModel, True) if len(inst_device_models) != 1: raise BadRequest("Instrument Device %s has multiple models associated %s", str(inst_device_id), str(len(inst_device_models))) log.debug("ObsMS:activate_deployment inst_device_model: %s", str(inst_device_models[0]) ) for inst_site_id in inst_site_ids: #get the model of this inst site, should be exactly one model attached inst_site_models, _ = self.clients.resource_registry.find_objects(inst_site_id, PRED.hasModel, RT.InstrumentModel, True) if len(inst_device_models) != 1: raise BadRequest("Instrument Site %s has multiple models associated: %s", str(inst_device_id), str(len(inst_device_models))) else: log.debug("ObsMS:activate_deployment inst_site_model: %s", str(inst_site_models[0]) ) #if the models match then deply this device into this site and remove this site from the list if inst_site_models[0] == inst_device_models[0]: self.deploy_device_to_site(inst_device_id, inst_site_id) log.debug("ObsMS:activate_deployment match found for instrument device: %s and site: %s", str(inst_device_id), str(inst_site_id)) #remove this site from the list of available instrument sites on the platform inst_site_ids.remove(inst_site_id) log.debug("ObsMS:activate_deployment instrument site list size: %s ", str(inst_site_ids)) break # go to the next inst_device on this platform and try to find a match #todo: throw an error if no match found? log.debug("ObsMS:activate_deployment No matching site found for instrument device: %s", str(inst_device_id)) return def deploy_device_to_site(self, device_id='', site_id=''): """ link a device to a site as the primary instrument """ #Check that the Site does not already have an associated primary device prim_device_ids, _ = self.clients.resource_registry.find_objects(site_id, PRED.hasDevice, RT.InstrumentDevice, True) if len(prim_device_ids) != 0: raise BadRequest("Site %s already has a primary device associated with id %s", str(site_id), str(prim_device_ids[0])) else: # Create the links self.clients.resource_registry.create_association(site_id, PRED.hasDevice, device_id) return def undeploy_device_from_site(self, device_id='', site_id=''): """ remove the link between a device and site which designates the instrument as primary """ #Check that the Site and Device are associated as primary device assoc_ids, _ = self.clients.resource_registry.find_associations(site_id, PRED.hasDevice, device_id, True) if len(assoc_ids) != 1: raise BadRequest("Site %s does not have device %s associated as the primary device", str(site_id), str(device_id)) else: # Create the links self.clients.resource_registry.delete_association(assoc_ids[0]) return ############################ # # ASSOCIATIONS # ############################ def assign_site_to_site(self, child_site_id='', parent_site_id=''): """Connects a child site (any subtype) to a parent site (any subtype) @param child_site_id str @param parent_site_id str @throws NotFound object with specified id does not exist """ parent_site_obj = self.subsite.read_one(parent_site_id) parent_site_type = parent_site_obj._get_type() if RT.Observatory == parent_site_type: self.observatory.link_site(parent_site_id, child_site_id) elif RT.Subsite == parent_site_type: self.subsite.link_site(parent_site_id, child_site_id) elif RT.PlatformSite == parent_site_type: self.platform_site.link_site(parent_site_id, child_site_id) else: raise BadRequest("Tried to assign a child site to a %s resource" % parent_site_type) def unassign_site_from_site(self, child_site_id='', parent_site_id=''): """Disconnects a child site (any subtype) from a parent site (any subtype) @param child_site_id str @param parent_site_id str @throws NotFound object with specified id does not exist """ parent_site_obj = self.subsite.read_one(parent_site_id) parent_site_type = parent_site_obj._get_type() if RT.Observatory == parent_site_type: self.observatory.unlink_site(parent_site_id, child_site_id) elif RT.Subsite == parent_site_type: self.subsite.unlink_site(parent_site_id, child_site_id) elif RT.PlatformSite == parent_site_type: self.platform_site.unlink_site(parent_site_id, child_site_id) else: raise BadRequest("Tried to unassign a child site from a %s resource" % parent_site_type) def assign_device_to_site(self, device_id='', site_id=''): """Connects a device (any type) to a site (any subtype) @param device_id str @param site_id str @throws NotFound object with specified id does not exist """ site_obj = self.subsite.read_one(site_id) site_type = site_obj._get_type() if RT.PlatformSite == site_type: self.platform_site.link_device(site_id, device_id) elif RT.InstrumentSite == site_type: self.instrument_site.link_device(site_id, device_id) else: raise BadRequest("Tried to assign a device to a %s resource" % site_type) def unassign_device_from_site(self, device_id='', site_id=''): """Disconnects a device (any type) from a site (any subtype) @param device_id str @param site_id str @throws NotFound object with specified id does not exist """ site_obj = self.subsite.read_one(site_id) site_type = site_obj._get_type() if RT.PlatformSite == site_type: self.platform_site.unlink_device(site_id, device_id) elif RT.InstrumentSite == site_type: self.instrument_site.unlink_device(site_id, device_id) else: raise BadRequest("Tried to unassign a device from a %s resource" % site_type) def assign_site_to_observatory(self, site_id='', observatory_id=''): self.observatory.link_site(observatory_id, site_id) def unassign_site_from_observatory(self, site_id="", observatory_id=''): self.observatory.unlink_site(observatory_id, site_id) def assign_instrument_model_to_instrument_site(self, instrument_model_id='', instrument_site_id=''): self.instrument_site.link_model(instrument_site_id, instrument_model_id) def unassign_instrument_model_from_instrument_site(self, instrument_model_id='', instrument_site_id=''): self.instrument_site.unlink_model(instrument_site_id, instrument_model_id) def assign_platform_model_to_platform_site(self, platform_model_id='', platform_site_id=''): self.platform_site.link_model(platform_site_id, platform_model_id) def unassign_platform_model_from_platform_site(self, platform_model_id='', platform_site_id=''): self.platform_site.unlink_model(platform_site_id, platform_model_id) def assign_resource_to_observatory_org(self, resource_id='', org_id=''): if not org_id: raise BadRequest("Org id not given") if not resource_id: raise BadRequest("Resource id not given") log.debug("assign_resource_to_observatory_org: org_id=%s, resource_id=%s " % (org_id, resource_id)) self.clients.org_management.share_resource(org_id, resource_id) def unassign_resource_from_observatory_org(self, resource_id='', org_id=''): if not org_id: raise BadRequest("Org id not given") if not resource_id: raise BadRequest("Resource id not given") self.clients.org_management.unshare_resource(org_id, resource_id) ########################################################################## # # DEPLOYMENTS # ########################################################################## def deploy_instrument_site(self, instrument_site_id='', deployment_id=''): self.instrument_site.link_deployment(instrument_site_id, deployment_id) def undeploy_instrument_site(self, instrument_site_id='', deployment_id=''): self.instrument_site.unlink_deployment(instrument_site_id, deployment_id) def deploy_platform_site(self, platform_site_id='', deployment_id=''): self.platform_site.link_deployment(platform_site_id, deployment_id) def undeploy_platform_site(self, platform_site_id='', deployment_id=''): self.platform_site.unlink_deployment(platform_site_id, deployment_id) ########################################################################## # # FIND OPS # ########################################################################## def find_org_by_observatory(self, observatory_id=''): """ """ orgs,_ = self.RR.find_subjects(RT.Org, PRED.hasResource, observatory_id, id_only=False) return orgs def find_related_frames_of_reference(self, input_resource_id='', output_resource_type_list=None): # the relative depth of each resource type in our tree depth = self.HIERARCHY_DEPTH input_obj = self.RR.read(input_resource_id) input_type = input_obj._get_type() #input type checking if not input_type in depth: raise BadRequest("Input resource type (got %s) must be one of %s" % (input_type, self.HIERARCHY_LOOKUP)) for t in output_resource_type_list: if not t in depth: raise BadRequest("Output resource types (got %s) must be one of %s" % (str(output_resource_type_list), self.HIERARCHY_LOOKUP)) subordinates = [x for x in output_resource_type_list if depth[x] > depth[input_type]] superiors = [x for x in output_resource_type_list if depth[x] < depth[input_type]] acc = {} acc[input_type] = [input_obj] if subordinates: # figure out the actual depth we need to go deepest_type = input_type #initial value for output_type in output_resource_type_list: if depth[deepest_type] < depth[output_type]: deepest_type = output_type log.debug("Deepest level for search will be '%s'" % deepest_type) acc = self._traverse_entity_tree(acc, input_type, deepest_type, True) if superiors: highest_type = input_type #initial value for output_type in output_resource_type_list: if depth[highest_type] > depth[output_type]: highest_type = output_type log.debug("Highest level for search will be '%s'" % highest_type) acc = self._traverse_entity_tree(acc, highest_type, input_type, False) # Don't include input type in response #TODO: maybe just remove the input resource id if input_type in acc: stripped = [] for r_obj in acc[input_type]: if r_obj._id == input_resource_id: log.debug("Stripped input from return value") else: stripped.append(r_obj) acc[input_type] = stripped return acc def _traverse_entity_tree(self, acc, top_type, bottom_type, downward): call_list = self._build_call_list(top_type, bottom_type, downward) # start calling functions if downward: for (p, c) in call_list: acc = self._find_subordinate(acc, p, c) else: for (p, c) in call_list: acc = self._find_superior(acc, p, c) return acc def _build_call_list(self, top_type, bottom_type, downward): call_list = [] if downward: step = 1 top_ord = self.HIERARCHY_DEPTH[top_type] bot_ord = self.HIERARCHY_DEPTH[bottom_type] else: step = -1 top_ord = self.HIERARCHY_DEPTH[bottom_type] - 1 bot_ord = self.HIERARCHY_DEPTH[top_type] - 1 for i in range(top_ord, bot_ord, step): child = self.HIERARCHY_LOOKUP[i + 1] parent = self.HIERARCHY_LOOKUP[i] if downward: tmp = [(parent, parent), (parent, child), (child, child)] else: tmp = [(child, child), (parent, child), (parent, parent)] for pair in tmp: if not pair in call_list: #log.debug("adding %s" % str(pair)) call_list.append(pair) return call_list def _find_subordinate(self, acc, parent_type, child_type): #acc is an accumulated dictionary find_fns = { (RT.Observatory, RT.Subsite): self.observatory.find_stemming_site, (RT.Subsite, RT.Subsite): self.subsite.find_stemming_subsite, (RT.Subsite, RT.PlatformSite): self.subsite.find_stemming_platform_site, (RT.PlatformSite, RT.PlatformSite): self.platform_site.find_stemming_platform_site, (RT.PlatformSite, RT.InstrumentSite): self.platform_site.find_stemming_instrument_site, } if (parent_type, child_type) in find_fns: find_fn = find_fns[(parent_type, child_type)] else: find_fn = (lambda x : []) if not parent_type in acc: acc[parent_type] = [] log.debug("Subordinates: '%s'x%d->'%s'" % (parent_type, len(acc[parent_type]), child_type)) #for all parents in the acc, add all their children for parent_obj in acc[parent_type]: parent_id = parent_obj._id for child_obj in find_fn(parent_id): actual_child_type = child_obj._get_type() if not actual_child_type in acc: acc[actual_child_type] = [] acc[actual_child_type].append(child_obj) return acc def _find_superior(self, acc, parent_type, child_type): # acc is an accumualted dictionary #log.debug("Superiors: '%s'->'%s'" % (parent_type, child_type)) #if True: # return acc find_fns = { (RT.Observatory, RT.Subsite): self.observatory.find_having_site, (RT.Subsite, RT.Subsite): self.subsite.find_having_site, (RT.Subsite, RT.PlatformSite): self.subsite.find_having_site, (RT.PlatformSite, RT.PlatformSite): self.platform_site.find_having_site, (RT.PlatformSite, RT.InstrumentSite): self.platform_site.find_having_site, } if (parent_type, child_type) in find_fns: find_fn = find_fns[(parent_type, child_type)] else: find_fn = (lambda x : []) if not child_type in acc: acc[child_type] = [] log.debug("Superiors: '%s'->'%s'x%d" % (parent_type, child_type, len(acc[child_type]))) #for all children in the acc, add all their parents for child_obj in acc[child_type]: child_id = child_obj._id for parent_obj in find_fn(child_id): actual_parent_type = parent_obj._get_type() if not actual_parent_type in acc: acc[actual_parent_type] = [] acc[actual_parent_type].append(parent_obj) return acc