def _update_allocations(self, dates_before, dates_after, reservation_id, reservation_status, host_reservation, values): min_hosts = self._convert_int_param( values.get('min', host_reservation['count_range'].split('-')[0]), 'min') max_hosts = self._convert_int_param( values.get('max', host_reservation['count_range'].split('-')[1]), 'max') if min_hosts < 0 or max_hosts < min_hosts: raise manager_ex.InvalidRange() hypervisor_properties = values.get( 'hypervisor_properties', host_reservation['hypervisor_properties']) resource_properties = values.get( 'resource_properties', host_reservation['resource_properties']) allocs = db_api.host_allocation_get_all_by_values( reservation_id=reservation_id) allocs_to_remove = self._allocations_to_remove(dates_before, dates_after, max_hosts, hypervisor_properties, resource_properties, allocs) if (allocs_to_remove and reservation_status == status.reservation.ACTIVE): raise manager_ex.NotEnoughHostsAvailable() kept_hosts = len(allocs) - len(allocs_to_remove) if kept_hosts < max_hosts: min_hosts = min_hosts - kept_hosts \ if (min_hosts - kept_hosts) > 0 else 0 max_hosts = max_hosts - kept_hosts host_ids = self._matching_hosts( hypervisor_properties, resource_properties, str(min_hosts) + '-' + str(max_hosts), dates_after['start_date'], dates_after['end_date']) if len(host_ids) >= min_hosts: pool = nova.ReservationPool() for host_id in host_ids: db_api.host_allocation_create({ 'compute_host_id': host_id, 'reservation_id': reservation_id }) if reservation_status == status.reservation.ACTIVE: # Add new host into the aggregate. new_host = db_api.host_get(host_id) pool.add_computehost(host_reservation['aggregate_id'], new_host['service_name']) else: raise manager_ex.NotEnoughHostsAvailable() for allocation in allocs_to_remove: db_api.host_allocation_destroy(allocation['id'])
def reserve_resource(self, reservation_id, values): """Create reservation.""" self._check_params(values) host_ids = self._matching_hosts( values['hypervisor_properties'], values['resource_properties'], values['count_range'], values['start_date'], values['end_date'], ) if not host_ids: raise manager_ex.NotEnoughHostsAvailable() pool = nova.ReservationPool() pool_name = reservation_id az_name = "%s%s" % (CONF[self.resource_type].blazar_az_prefix, pool_name) pool_instance = pool.create(name=pool_name, az=az_name) host_rsrv_values = { 'reservation_id': reservation_id, 'aggregate_id': pool_instance.id, 'resource_properties': values['resource_properties'], 'hypervisor_properties': values['hypervisor_properties'], 'count_range': values['count_range'], 'status': 'pending', 'before_end': values['before_end'] } host_reservation = db_api.host_reservation_create(host_rsrv_values) for host_id in host_ids: db_api.host_allocation_create({'compute_host_id': host_id, 'reservation_id': reservation_id}) return host_reservation['id']
def pickup_hosts(self, reservation_id, values): """Returns lists of host ids to add/remove. This function picks up available hosts, calculates the difference from old reservations and returns a dict of a list of host ids to add and remove keyed by "added" or "removed". Note that the lists allow duplicated host ids for `affinity=True` cases. :raises: NotEnoughHostsAvailable exception if there are not enough hosts available for the request """ req_amount = values['amount'] affinity = bool_from_string(values['affinity'], default=None) query_params = { 'cpus': values['vcpus'], 'memory': values['memory_mb'], 'disk': values['disk_gb'], 'resource_properties': values['resource_properties'], 'start_date': values['start_date'], 'end_date': values['end_date'] } old_allocs = db_api.host_allocation_get_all_by_values( reservation_id=reservation_id) if old_allocs: # This is a path for *update* reservation. Add the specific # query param not to consider resources reserved by existing # reservations to update query_params['excludes_res'] = [reservation_id] new_hosts = self.query_available_hosts(**query_params) old_host_id_list = [h['compute_host_id'] for h in old_allocs] candidate_id_list = [h['id'] for h in new_hosts] # Build `new_host_id_list`. Note that we'd like to pick up hosts in # the following order of priority: # 1. hosts reserved by the reservation to update # 2. hosts with reservations followed by hosts without reservations # Note that the `candidate_id_list` has already been ordered # satisfying the second requirement. if affinity: host_id_map = collections.Counter(candidate_id_list) available = {k for k, v in host_id_map.items() if v >= req_amount} if not available: raise mgr_exceptions.NotEnoughHostsAvailable() new_host_ids = set(old_host_id_list) & available if new_host_ids: # (priority 1) This is a path for update reservation. We pick # up a host from hosts reserved by the reservation to update. new_host_id = new_host_ids.pop() else: # (priority 2) This is a path both for update and for new # reservation. We pick up hosts with some other reservations # if possible and otherwise pick up hosts without any # reservation. We can do so by considering the order of the # `candidate_id_list`. for host_id in candidate_id_list: if host_id in available: new_host_id = host_id break new_host_id_list = [new_host_id] * req_amount else: # Hosts that can accommodate but don't satisfy priority 1 _, possible_host_list = plugins_utils.list_difference( old_host_id_list, candidate_id_list) # Hosts that satisfy priority 1 new_host_id_list, _ = plugins_utils.list_difference( candidate_id_list, possible_host_list) if affinity is False: # Eliminate the duplication new_host_id_list = list(set(new_host_id_list)) for host_id in possible_host_list: if (affinity is False) and (host_id in new_host_id_list): # Eliminate the duplication continue new_host_id_list.append(host_id) if len(new_host_id_list) < req_amount: raise mgr_exceptions.NotEnoughHostsAvailable() while len(new_host_id_list) > req_amount: new_host_id_list.pop() # Calculate the difference from the existing reserved host removed_host_ids, added_host_ids = plugins_utils.list_difference( old_host_id_list, new_host_id_list) return {'added': added_host_ids, 'removed': removed_host_ids}
def update_reservation(self, reservation_id, new_values): """Updates an instance reservation with requested parameters. This method allows users to update an instance reservation under the following conditions. - If an instance reservation has not started yet - vcpus, memory_mb disk_gb and amount can be updateable unless Blazar can accommodate the new request. - If an instance reservation has already started - only amount is increasable. """ # TODO(masahito) the instance reservation plugin only supports # anti-affinity rule in short-term goal. if bool_from_string(new_values.get('affinity', None)): raise mgr_exceptions.MalformedParameter( param='affinity (only affinity = False is supported)') reservation = db_api.reservation_get(reservation_id) lease = db_api.lease_get(reservation['lease_id']) updatable = ['vcpus', 'memory_mb', 'disk_gb', 'affinity', 'amount', 'resource_properties'] if (not any([k in updatable for k in new_values.keys()]) and new_values['start_date'] >= lease['start_date'] and new_values['end_date'] <= lease['end_date']): # no update because of just shortening the reservation time return if (reservation['status'] == 'active' and any([k in updatable[:-1] for k in new_values.keys()])): msg = "An active reservation only accepts to update amount." raise mgr_exceptions.InvalidStateUpdate(msg) if reservation['status'] == 'error': msg = "An error reservation doesn't accept an updating request." raise mgr_exceptions.InvalidStateUpdate(msg) for key in updatable: if key not in new_values: new_values[key] = reservation[key] changed_hosts = self.pickup_hosts(reservation_id, new_values) if (reservation['status'] == 'active' and len(changed_hosts['removed']) > 0): err_msg = ("Instance reservation doesn't allow to reduce/replace " "reserved instance slots when the reservation is in " "active status.") raise mgr_exceptions.CantUpdateParameter(err_msg) if (new_values['amount'] - reservation['amount'] != (len(changed_hosts['added']) - len(changed_hosts['removed']))): raise mgr_exceptions.NotEnoughHostsAvailable() db_api.instance_reservation_update( reservation['resource_id'], {key: new_values[key] for key in updatable}) self.update_host_allocations(changed_hosts['added'], changed_hosts['removed'], reservation_id) try: self.update_resources(reservation_id) except mgr_exceptions.NovaClientError: raise