def update_statuses(self, callback, *args, **kwargs): """Update leases and reservations table after executing a callback.""" try: # The callback() has to return a dictionary of # {reservation id: flags to update}. # e.g. {'dummyid': {'missing_resources': True}} reservation_flags = callback(*args, **kwargs) except Exception as e: LOG.exception( 'Caught an exception while executing a callback. ' '%s', str(e)) # TODO(hiro-kobayashi): update statuses of related leases and # reservations. Depends on the state-machine blueprint. # Update flags of related leases and reservations. lease_ids = set([]) for reservation_id, flags in reservation_flags.items(): db_api.reservation_update(reservation_id, flags) LOG.debug('Reservation %s was updated: %s', reservation_id, flags) reservation = db_api.reservation_get(reservation_id) lease_ids.add(reservation['lease_id']) for lease_id in lease_ids: LOG.debug('Lease %s was updated: {"degraded": True}', lease_id) db_api.lease_update(lease_id, {'degraded': True})
def _reallocate(self, allocation): """Allocate an alternative host. :param: allocation: allocation to change. :return: True if an alternative host was successfully allocated. """ reservation = db_api.reservation_get(allocation['reservation_id']) pool = nova.ReservationPool() # Remove the failed host from the aggregate. if reservation['status'] == status.reservation.ACTIVE: host = db_api.host_get(allocation['compute_host_id']) pool.remove_computehost(reservation['aggregate_id'], host['service_name']) try: self.placement_client.delete_reservation_inventory( host['service_name'], reservation['id']) except openstack_ex.ResourceProviderNotFound: pass # Allocate an alternative host. values = {} lease = db_api.lease_get(reservation['lease_id']) values['start_date'] = max(datetime.datetime.utcnow(), lease['start_date']) values['end_date'] = lease['end_date'] specs = ['vcpus', 'memory_mb', 'disk_gb', 'affinity', 'amount', 'resource_properties'] for key in specs: values[key] = reservation[key] changed_hosts = self.pickup_hosts(reservation['id'], values) if len(changed_hosts['added']) == 0: db_api.host_allocation_destroy(allocation['id']) LOG.warn('Could not find alternative host for reservation %s ' '(lease: %s).', reservation['id'], lease['name']) return False else: new_host_id = changed_hosts['added'].pop() db_api.host_allocation_update( allocation['id'], {'compute_host_id': new_host_id}) if reservation['status'] == status.reservation.ACTIVE: # Add the alternative host into the aggregate. new_host = db_api.host_get(new_host_id) pool.add_computehost(reservation['aggregate_id'], new_host['service_name'], stay_in=True) self.placement_client.update_reservation_inventory( new_host['service_name'], reservation['id'], 1) LOG.warn('Resource changed for reservation %s (lease: %s).', reservation['id'], lease['name']) return True
def _update_flags(self, reservation_flags): """Update lease/reservation flags.""" lease_ids = set([]) for reservation_id, flags in reservation_flags.items(): db_api.reservation_update(reservation_id, flags) LOG.debug('Reservation %s was updated: %s', reservation_id, flags) reservation = db_api.reservation_get(reservation_id) lease_ids.add(reservation['lease_id']) for lease_id in lease_ids: LOG.debug('Lease %s was updated: {"degraded": True}', lease_id) db_api.lease_update(lease_id, {'degraded': True})
def update_resources(self, reservation_id): """Updates reserved resources in Nova. This method updates reserved resources in Compute service. If the reservation is in active status, it adds new allocated hosts into a reserved aggregate. If the reservation is not started yet, it updates a reserved flavor. """ reservation = db_api.reservation_get(reservation_id) if reservation['status'] == 'active': pool = nova.ReservationPool() # Dict of number of instances to reserve on a host keyed by the # host id allocation_map = collections.defaultdict(lambda: 0) for allocation in db_api.host_allocation_get_all_by_values( reservation_id=reservation['id']): host_id = allocation['compute_host_id'] allocation_map[host_id] += 1 for host_id, num in allocation_map.items(): host = db_api.host_get(host_id) try: pool.add_computehost(reservation['aggregate_id'], host['service_name'], stay_in=True) except mgr_exceptions.AggregateAlreadyHasHost: pass except nova_exceptions.ClientException: err_msg = ('Fail to add host %s to aggregate %s.' % (host, reservation['aggregate_id'])) raise mgr_exceptions.NovaClientError(err_msg) self.placement_client.update_reservation_inventory( host['hypervisor_hostname'], reservation['id'], num) else: try: self.nova.nova.flavors.delete(reservation['id']) self._create_flavor(reservation['id'], reservation['vcpus'], reservation['memory_mb'], reservation['disk_gb'], reservation['server_group_id']) except nova_exceptions.ClientException: LOG.exception( "Failed to update Nova resources " "for reservation %s", reservation['id']) raise mgr_exceptions.NovaClientError()
def update_reservation(self, reservation_id, values): """Update reservation.""" reservation = db_api.reservation_get(reservation_id) lease = db_api.lease_get(reservation['lease_id']) dates_before = { 'start_date': lease['start_date'], 'end_date': lease['end_date'] } dates_after = { 'start_date': values['start_date'], 'end_date': values['end_date'] } fip_reservation = db_api.fip_reservation_get( reservation['resource_id']) if ('network_id' in values and values.get('network_id') != fip_reservation['network_id']): raise manager_ex.CantUpdateFloatingIPReservation( msg="Updating network_id is not supported") required_fips = fip_reservation['required_floatingips'] if ('required_floatingips' in values and values['required_floatingips'] != required_fips and values['required_floatingips'] != []): raise manager_ex.CantUpdateFloatingIPReservation( msg="Updating required_floatingips is not supported except " "with an empty list") self._update_allocations(dates_before, dates_after, reservation_id, reservation['status'], fip_reservation, values) updates = {} if 'amount' in values: updates['amount'] = values.get('amount') if updates: db_api.fip_reservation_update(fip_reservation['id'], updates) if ('required_floatingips' in values and values['required_floatingips'] != required_fips): db_api.required_fip_destroy_by_fip_reservation_id( fip_reservation['id']) for fip_address in values.get('required_floatingips'): fip_address_values = { 'address': fip_address, 'floatingip_reservation_id': fip_reservation['id'] } db_api.required_fip_create(fip_address_values)
def _reallocate(self, allocation): """Allocate an alternative host. :param: allocation: allocation to change. :return: True if an alternative host was successfully allocated. """ reservation = db_api.reservation_get(allocation['reservation_id']) h_reservation = db_api.host_reservation_get( reservation['resource_id']) lease = db_api.lease_get(reservation['lease_id']) pool = nova.ReservationPool() # Remove the old host from the aggregate. if reservation['status'] == status.reservation.ACTIVE: host = db_api.host_get(allocation['compute_host_id']) pool.remove_computehost(h_reservation['aggregate_id'], host['service_name']) # Allocate an alternative host. start_date = max(datetime.datetime.utcnow(), lease['start_date']) new_hostids = self._matching_hosts( reservation['hypervisor_properties'], reservation['resource_properties'], '1-1', start_date, lease['end_date'] ) if not new_hostids: db_api.host_allocation_destroy(allocation['id']) LOG.warn('Could not find alternative host for reservation %s ' '(lease: %s).', reservation['id'], lease['name']) return False else: new_hostid = new_hostids.pop() db_api.host_allocation_update(allocation['id'], {'compute_host_id': new_hostid}) LOG.warn('Resource changed for reservation %s (lease: %s).', reservation['id'], lease['name']) if reservation['status'] == status.reservation.ACTIVE: # Add the alternative host into the aggregate. new_host = db_api.host_get(new_hostid) pool.add_computehost(h_reservation['aggregate_id'], new_host['service_name']) return True
def update_reservation(self, reservation_id, values): """Update reservation.""" reservation = db_api.reservation_get(reservation_id) lease = db_api.lease_get(reservation['lease_id']) if (not [ x for x in values.keys() if x in ['min', 'max', 'hypervisor_properties', 'resource_properties'] ] and values['start_date'] >= lease['start_date'] and values['end_date'] <= lease['end_date']): # Nothing to update return dates_before = { 'start_date': lease['start_date'], 'end_date': lease['end_date'] } dates_after = { 'start_date': values['start_date'], 'end_date': values['end_date'] } host_reservation = db_api.host_reservation_get( reservation['resource_id']) self._update_allocations(dates_before, dates_after, reservation_id, reservation['status'], host_reservation, values) updates = {} if 'min' in values or 'max' in values: count_range = str( values.get('min', host_reservation['count_range'].split( '-')[0])) + '-' + str( values.get( 'max', host_reservation['count_range'].split('-')[1])) updates['count_range'] = count_range if 'hypervisor_properties' in values: updates['hypervisor_properties'] = values.get( 'hypervisor_properties') if 'resource_properties' in values: updates['resource_properties'] = values.get('resource_properties') if updates: db_api.host_reservation_update(host_reservation['id'], updates)
def update_resources(self, reservation_id): """Updates reserved resources in Nova. This method updates reserved resources in Compute service. If the reservation is in active status, it adds new allocated hosts into a reserved aggregate. If the reservation is not started yet, it updates a reserved flavor. """ reservation = db_api.reservation_get(reservation_id) if reservation['status'] == 'active': pool = nova.ReservationPool() for allocation in db_api.host_allocation_get_all_by_values( reservation_id=reservation['id']): host = db_api.host_get(allocation['compute_host_id']) try: pool.add_computehost(reservation['aggregate_id'], host['service_name'], stay_in=True) except mgr_exceptions.AggregateAlreadyHasHost: pass except nova_exceptions.ClientException: err_msg = ('Fail to add host %s to aggregate %s.' % (host, reservation['aggregate_id'])) raise mgr_exceptions.NovaClientError(err_msg) else: try: self.nova.nova.flavors.delete(reservation['id']) self._create_flavor(reservation['id'], reservation['vcpus'], reservation['memory_mb'], reservation['disk_gb'], reservation['server_group_id']) except nova_exceptions.ClientException: LOG.exception( "Failed to update Nova resources " "for reservation %s", reservation['id']) raise mgr_exceptions.NovaClientError()
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. """ self._validate_reservation_params(new_values) 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) if new_values.get('affinity', None): new_values['affinity'] = bool_from_string(new_values['affinity'], default=None) 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) 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
def heal_reservations(self, failed_resources): """Heal reservations which suffer from resource failures. :param: failed_resources: a list of failed hosts. :return: a dictionary of {reservation id: flags to update} e.g. {'de27786d-bd96-46bb-8363-19c13b2c6657': {'missing_resources': True}} """ reservation_flags = {} failed_allocs = [] for host in failed_resources: failed_allocs += db_api.host_allocation_get_all_by_values( compute_host_id=host['id']) for alloc in failed_allocs: reservation = db_api.reservation_get(alloc['reservation_id']) if reservation['resource_type'] != plugin.RESOURCE_TYPE: continue lease = db_api.lease_get(reservation['lease_id']) host_reservation = None pool = None # Remove the failed host from the aggregate. if reservation['status'] == status.reservation.ACTIVE: host = db_api.host_get(alloc['compute_host_id']) host_reservation = db_api.host_reservation_get( reservation['resource_id']) with trusts.create_ctx_from_trust(lease['trust_id']): pool = nova.ReservationPool() pool.remove_computehost(host_reservation['aggregate_id'], host['service_name']) # Allocate alternative resource. start_date = max(datetime.datetime.utcnow(), lease['start_date']) new_hostids = self._matching_hosts( reservation['hypervisor_properties'], reservation['resource_properties'], '1-1', start_date, lease['end_date']) if not new_hostids: if reservation['id'] not in reservation_flags: reservation_flags[reservation['id']] = {} reservation_flags[reservation['id']].update( {'missing_resources': True}) db_api.host_allocation_destroy(alloc['id']) LOG.warn( 'Could not find alternative host for reservation %s ' '(lease: %s).', reservation['id'], lease['name']) else: new_hostid = new_hostids.pop() db_api.host_allocation_update(alloc['id'], {'compute_host_id': new_hostid}) if reservation['status'] == status.reservation.ACTIVE: # Add the alternative host into the aggregate. new_host = db_api.host_get(new_hostid) with trusts.create_ctx_from_trust(lease['trust_id']): pool.add_computehost(host_reservation['aggregate_id'], new_host['service_name']) if reservation['id'] not in reservation_flags: reservation_flags[reservation['id']] = {} reservation_flags[reservation['id']].update( {'resources_changed': True}) LOG.warn('Resource changed for reservation %s (lease: %s).', reservation['id'], lease['name']) return reservation_flags
def heal_reservations(self, failed_resources): """Heal reservations which suffer from resource failures. :param: failed_resources: a list of failed hosts. :return: a dictionary of {reservation id: flags to update} e.g. {'de27786d-bd96-46bb-8363-19c13b2c6657': {'missing_resources': True}} """ reservation_flags = {} failed_allocs = [] for host in failed_resources: failed_allocs += db_api.host_allocation_get_all_by_values( compute_host_id=host['id']) for alloc in failed_allocs: reservation = db_api.reservation_get(alloc['reservation_id']) if reservation['resource_type'] != RESOURCE_TYPE: continue pool = None # Remove the failed host from the aggregate. if reservation['status'] == status.reservation.ACTIVE: host = db_api.host_get(alloc['compute_host_id']) pool = nova.ReservationPool() pool.remove_computehost(reservation['aggregate_id'], host['service_name']) # Allocate alternative resource. values = {} lease = db_api.lease_get(reservation['lease_id']) values['start_date'] = max(datetime.datetime.utcnow(), lease['start_date']) values['end_date'] = lease['end_date'] specs = ['vcpus', 'memory_mb', 'disk_gb', 'affinity', 'amount'] for key in specs: values[key] = reservation[key] changed_hosts = self.pickup_hosts(reservation['id'], values) if len(changed_hosts['added']) == 0: if reservation['id'] not in reservation_flags: reservation_flags[reservation['id']] = {} reservation_flags[reservation['id']].update( {'missing_resources': True}) db_api.host_allocation_destroy(alloc['id']) LOG.warn( 'Could not find alternative host for reservation %s ' '(lease: %s).', reservation['id'], lease['name']) else: new_host_id = changed_hosts['added'].pop() db_api.host_allocation_update(alloc['id'], {'compute_host_id': new_host_id}) if reservation['status'] == status.reservation.ACTIVE: # Add the alternative host into the aggregate. new_host = db_api.host_get(new_host_id) pool.add_computehost(reservation['aggregate_id'], new_host['service_name'], stay_in=True) if reservation['id'] not in reservation_flags: reservation_flags[reservation['id']] = {} reservation_flags[reservation['id']].update( {'resources_changed': True}) LOG.warn('Resource changed for reservation %s (lease: %s).', reservation['id'], lease['name']) return reservation_flags