def _get_network_running_observations(self, tels, ends_after, starts_before): n_running_total = 0 running_at_tel = {} for full_tel_name in tels: tel_name, obs_name, site_name = full_tel_name.split('.') log.debug( "Acquiring running observations and first availability at %s", full_tel_name) running = self._get_running_observations(ends_after, starts_before, site_name, obs_name, tel_name) running_at_tel[full_tel_name] = running n_running = len(running) _, observation_str = pl(n_running, 'observation') log.debug("Found %d running %s at %s", n_running, observation_str, full_tel_name) n_running_total += n_running _, observation_str = pl(n_running_total, 'observation') log.info("Network-wide, found %d running %s", n_running_total, observation_str) return running_at_tel
def _send_schedule_to_observation_portal(self, schedule, semester_start, configdb_interface, dry_run=False): ''' Convert a kernel schedule into Observation Portal observations and submit them ''' # TODO: Update this code to send Observations and ConfigStatuses to observation portal observations_by_resource = defaultdict(list) for resource_name, reservations in schedule.items(): for reservation in reservations: try: observation = build_observation(reservation, semester_start, configdb_interface) except Exception: log.exception( 'Unable to build observation from reservation for request number {}' .format(reservation.request.id)) continue observations_by_resource[resource_name].append(observation) _, observation_str = pl( len(observations_by_resource[resource_name]), 'observation') msg = 'Will send {} {} to {}'.format( len(observations_by_resource[resource_name]), observation_str, resource_name) log_info_dry_run(msg, dry_run) n_submitted_total = self._send_observations_to_observation_portal( observations_by_resource, dry_run) return n_submitted_total
def filter_on_scheduling_horizon(request_groups, scheduling_horizon): '''Filter out windows in user requests that extend beyond the scheduling horizon for types (single, many) ''' rgs_by_type = filter_compounds_by_type(request_groups) log.info("Identified %s, %s, %s, %s" % (pl(len(rgs_by_type['single']), 'single'), pl(len(rgs_by_type['many']), 'many'), pl(len(rgs_by_type['and']), 'and'), pl(len(rgs_by_type['oneof']), 'oneof'))) # Filter windows that are beyond the short-term scheduling horizon log.info("Filtering RGs of type 'single' and 'many' based on scheduling horizon (%s)" % scheduling_horizon) horizon_limited_rgs = rgs_by_type['single'] + rgs_by_type['many'] horizon_limited_rgs = truncate_upper_crossing_windows(horizon_limited_rgs, horizon=scheduling_horizon) horizon_limited_rgs = filter_out_future_windows(horizon_limited_rgs, horizon=scheduling_horizon) # TODO: Add the duration filter here? # Clean up Requests without any windows horizon_limited_rgs = filter_on_type(horizon_limited_rgs) # Many's may have children with no windows that should be removed from consideration drop_empty_requests(horizon_limited_rgs) log.info("After filtering, %d horizon-limited rgs remain" % len(horizon_limited_rgs)) # Compounds (and/oneof) are not constrained to the short-term scheduling horizon # TODO: Remove this block after review log.info("Filtering compound RGs of type 'and' and 'oneof', not constrained by scheduling horizon") unlimited_rgs = rgs_by_type['and'] + rgs_by_type['oneof'] unlimited_rgs = truncate_upper_crossing_windows(unlimited_rgs) unlimited_rgs = filter_out_future_windows(unlimited_rgs) # TODO: it's possible that one-ofs and ands may have these windowless # children at this point from requests that crossed the semester boundary # might need to drop empty requests before filtering on type # Clean up Requests without any windows unlimited_rgs = filter_on_type(unlimited_rgs) log.info("After filtering, %d unlimited RGs remain" % len(unlimited_rgs)) remaining_rgs = horizon_limited_rgs + unlimited_rgs return remaining_rgs
def build_request_group(self, rg_dict, scheduled_requests=None, ignore_ipp=False): if scheduled_requests is None: scheduled_requests = {} rg_id = int(rg_dict['id']) operator = rg_dict['operator'].lower() ipp_value = rg_dict.get('ipp_value', 1.0) submitter = rg_dict.get('submitter', '') if ignore_ipp: # if we want to ignore ipp in the scheduler, then set it to 1.0 here and it will not modify the priority ipp_value = 1.0 requests, invalid_requests = self.build_requests(rg_dict, scheduled_requests, is_staff=rg_dict.get( 'is_staff', False)) if invalid_requests: msg = "Found %s." % pl(len(invalid_requests), 'invalid Request') log.warn(msg) for _, error_msg in invalid_requests: tag = "InvalidRequest" RequestGroup.emit_request_group_feedback(rg_id, error_msg, tag) if operator.lower() == 'and': msg = "Invalid request found within 'AND' RG %s making RG invalid" % rg_id tag = "InvalidRequestGroup" RequestGroup.emit_request_group_feedback(rg_id, msg, tag) raise RequestError(msg) if not requests: msg = "No valid Requests for RG %s" % rg_id tag = "InvalidRequestGroup" RequestGroup.emit_request_group_feedback(rg_id, msg, tag) raise RequestError(msg) proposal = self.get_proposal_details(rg_dict['proposal']) # Validate we are an allowed type of UR valid_observation_types = ['NORMAL', 'RAPID_RESPONSE', 'TIME_CRITICAL'] observation_type = rg_dict['observation_type'] if observation_type not in valid_observation_types: msg = "RequestGroup observation_type must be one of %s" % valid_observation_types raise RequestError(msg) # Calculate the maximum window time as the expire time max_window_time = datetime(1000, 1, 1) for req in requests: for windows in req.windows.windows_for_resource.values(): for window in windows: max_window_time = max(max_window_time, window.end) # Truncate the expire time by the current semester's end semester_details = self.get_semester_details(datetime.utcnow()) if semester_details: max_window_time = min(max_window_time, semester_details['end']) request_group = RequestGroup( operator=operator, requests=requests, proposal=proposal, rg_id=rg_id, is_staff=rg_dict.get('is_staff', False), observation_type=observation_type, ipp_value=ipp_value, name=rg_dict['name'], expires=max_window_time, submitter=safe_unidecode(submitter, 50), ) # Return only the invalid request and not the error message invalid_requests = [ir[0] for ir in invalid_requests] return request_group, invalid_requests