def delete(self, subcloud_ref): """Delete a subcloud. :param subcloud_ref: ID or name of subcloud to delete. """ context = restcomm.extract_context_from_environ() subcloud = None if subcloud_ref.isdigit(): # Look up subcloud as an ID try: subcloud = db_api.subcloud_get(context, subcloud_ref) except exceptions.SubcloudNotFound: pecan.abort(404, _('Subcloud not found')) else: # Look up subcloud by name try: subcloud = db_api.subcloud_get_by_name(context, subcloud_ref) except exceptions.SubcloudNameNotFound: pecan.abort(404, _('Subcloud not found')) subcloud_id = subcloud.id try: # Ask dcmanager-manager to delete the subcloud. # It will do all the real work... return self.rpc_client.delete_subcloud(context, subcloud_id) except RemoteError as e: pecan.abort(422, e.value) except Exception as e: LOG.exception(e) pecan.abort(500, _('Unable to delete subcloud'))
def patch(self, subcloud_ref=None): """Update a subcloud. :param subcloud_ref: ID or name of subcloud to update """ context = restcomm.extract_context_from_environ() subcloud = None if subcloud_ref is None: pecan.abort(400, _('Subcloud ID required')) payload = eval(request.body) if not payload: pecan.abort(400, _('Body required')) if subcloud_ref.isdigit(): # Look up subcloud as an ID try: subcloud = db_api.subcloud_get(context, subcloud_ref) except exceptions.SubcloudNotFound: pecan.abort(404, _('Subcloud not found')) else: # Look up subcloud by name try: subcloud = db_api.subcloud_get_by_name(context, subcloud_ref) except exceptions.SubcloudNameNotFound: pecan.abort(404, _('Subcloud not found')) subcloud_id = subcloud.id management_state = payload.get('management-state') description = payload.get('description') location = payload.get('location') if not (management_state or description or location): pecan.abort(400, _('nothing to update')) # Syntax checking if management_state and \ management_state not in [consts.MANAGEMENT_UNMANAGED, consts.MANAGEMENT_MANAGED]: pecan.abort(400, _('Invalid management-state')) try: # Inform dcmanager-manager that subcloud has been updated. # It will do all the real work... subcloud = self.rpc_client.update_subcloud( context, subcloud_id, management_state=management_state, description=description, location=location) return subcloud except RemoteError as e: pecan.abort(422, e.value) except Exception as e: # additional exceptions. LOG.exception(e) pecan.abort(500, _('Unable to update subcloud'))
def get(self, subcloud_ref=None): """Get details about software update options. :param subcloud: name or id of subcloud (optional) """ context = restcomm.extract_context_from_environ() if subcloud_ref is None: # List of all subcloud options requested. # Prepend the all clouds default options to the result. result = dict() result['sw-update-options'] = list() default_sw_update_opts_dict = utils.get_sw_update_opts(context) result['sw-update-options'].append(default_sw_update_opts_dict) subclouds = db_api.sw_update_opts_get_all_plus_subcloud_info( context) for subcloud, sw_update_opts in subclouds: if sw_update_opts: result['sw-update-options'].append( db_api.sw_update_opts_w_name_db_model_to_dict( sw_update_opts, subcloud.name)) return result elif subcloud_ref == consts.DEFAULT_REGION_NAME: # Default options requested, guaranteed to succeed return utils.get_sw_update_opts(context) else: # Specific subcloud options requested if subcloud_ref.isdigit(): # Look up subcloud as an ID try: subcloud = db_api.subcloud_get(context, subcloud_ref) except exceptions.SubcloudNotFound: pecan.abort(404, _('Subcloud not found')) else: # Look up subcloud by name try: subcloud = db_api.subcloud_get_by_name( context, subcloud_ref) except exceptions.SubcloudNameNotFound: pecan.abort(404, _('Subcloud not found')) try: return utils.get_sw_update_opts(context, subcloud_id=subcloud.id) except Exception as e: pecan.abort(404, _('%s') % e)
def delete(self, subcloud_ref): """Delete the software update options.""" context = restcomm.extract_context_from_environ() if subcloud_ref == consts.DEFAULT_REGION_NAME: # Delete defaults. # Note by deleting these, the next get will repopulate with # the global constants. try: db_api.sw_update_opts_default_destroy(context) except Exception: return else: if subcloud_ref.isdigit(): # Look up subcloud as an ID try: subcloud = db_api.subcloud_get(context, subcloud_ref) except exceptions.SubcloudNotFound: pecan.abort(404, _('Subcloud not found')) else: # Look up subcloud by name try: subcloud = db_api.subcloud_get_by_name( context, subcloud_ref) except exceptions.SubcloudNameNotFound: pecan.abort(404, _('Subcloud not found')) # Delete the subcloud specific options if db_api.sw_update_opts_get(context, subcloud.id): db_api.sw_update_opts_destroy(context, subcloud.id) else: pecan.abort(404, _('Subcloud patch options not found'))
def post(self, subcloud_ref=None, qualifier=None): """Create a new subcloud. :param subcloud_ref: ID of or name subcloud (only used when generating config) :param qualifier: if 'config', returns the config INI file for the subcloud """ context = restcomm.extract_context_from_environ() if subcloud_ref is None: payload = eval(request.body) if not payload: pecan.abort(400, _('Body required')) name = payload.get('name') if not name: pecan.abort(400, _('name required')) management_subnet = payload.get('management-subnet') if not management_subnet: pecan.abort(400, _('management-subnet required')) management_start_ip = payload.get('management-start-ip') if not management_start_ip: pecan.abort(400, _('management-start-ip required')) management_end_ip = payload.get('management-end-ip') if not management_end_ip: pecan.abort(400, _('management-end-ip required')) management_gateway_ip = payload.get('management-gateway-ip') if not management_gateway_ip: pecan.abort(400, _('management-gateway-ip required')) systemcontroller_gateway_ip = \ payload.get('systemcontroller-gateway-ip') if not systemcontroller_gateway_ip: pecan.abort(400, _('systemcontroller-gateway-ip required')) self._validate_subcloud_config(context, name, management_subnet, management_start_ip, management_end_ip, management_gateway_ip, systemcontroller_gateway_ip) try: # Ask dcmanager-manager to add the subcloud. # It will do all the real work... return self.rpc_client.add_subcloud(context, payload) except RemoteError as e: pecan.abort(422, e.value) except Exception as e: LOG.exception(e) pecan.abort(500, _('Unable to create subcloud')) elif qualifier: if qualifier == 'config': subcloud = None if subcloud_ref.isdigit(): # Look up subcloud as an ID try: subcloud = db_api.subcloud_get(context, subcloud_ref) except exceptions.SubcloudNotFound: pecan.abort(404, _('Subcloud not found')) else: # Look up subcloud by name try: subcloud = db_api.subcloud_get_by_name( context, subcloud_ref) except exceptions.SubcloudNameNotFound: pecan.abort(404, _('Subcloud not found')) payload = dict() if request.body: payload = eval(request.body) config_file = self._create_subcloud_config_file( context, subcloud, payload) result = dict() result['config'] = config_file return result else: pecan.abort(400, _('Invalid request')) else: pecan.abort(400, _('Invalid request'))
def get(self, subcloud_ref=None, qualifier=None): """Get details about subcloud. :param subcloud_ref: ID or name of subcloud """ context = restcomm.extract_context_from_environ() if subcloud_ref is None: # List of subclouds requested subclouds = db_api.subcloud_get_all_with_status(context) result = dict() result['subclouds'] = [] first_time = True subcloud_list = [] subcloud_status_list = [] # We get back a subcloud, subcloud_status pair for every # subcloud_status entry corresponding to a subcloud. (Subcloud # info repeats) # Aggregate all the sync status for each of the # endpoints per subcloud into an overall sync status for subcloud, subcloud_status in subclouds: subcloud_dict = db_api.subcloud_db_model_to_dict(subcloud) subcloud_status_dict = db_api.subcloud_status_db_model_to_dict( subcloud_status) subcloud_dict.update(subcloud_status_dict) if not first_time: if subcloud_list[-1]['id'] == subcloud_dict['id']: # We have a match for this subcloud id already, # check if we have a same sync_status if subcloud_list[-1][consts.SYNC_STATUS] != \ subcloud_dict[consts.SYNC_STATUS]: subcloud_list[-1][consts.SYNC_STATUS] = \ consts.SYNC_STATUS_OUT_OF_SYNC if subcloud_status: subcloud_status_list.append( db_api. subcloud_endpoint_status_db_model_to_dict( # noqa subcloud_status)) subcloud_list[-1][ consts.ENDPOINT_SYNC_STATUS] = subcloud_status_list else: subcloud_status_list = [] if subcloud_status: subcloud_status_list.append( db_api. subcloud_endpoint_status_db_model_to_dict( # noqa subcloud_status)) subcloud_list.append(subcloud_dict) else: if subcloud_status: subcloud_status_list.append( db_api.subcloud_endpoint_status_db_model_to_dict( subcloud_status)) subcloud_list.append(subcloud_dict) first_time = False for s in subcloud_list: result['subclouds'].append(s) return result else: # Single subcloud requested subcloud = None subcloud_dict = dict() subcloud_status_list = [] endpoint_sync_dict = dict() if subcloud_ref.isdigit(): # Look up subcloud as an ID try: subcloud = db_api.subcloud_get(context, subcloud_ref) except exceptions.SubcloudNotFound: pecan.abort(404, _('Subcloud not found')) else: # Look up subcloud by name try: subcloud = db_api.subcloud_get_by_name( context, subcloud_ref) except exceptions.SubcloudNameNotFound: pecan.abort(404, _('Subcloud not found')) subcloud_id = subcloud.id if qualifier: # Configuration for this subcloud requested. # Encrypt before sending. if qualifier == 'config': result = dict() user_list = self._get_subcloud_users() # Use a hash of the subcloud name + management subnet # as the encryption key hashstring = subcloud.name + subcloud.management_subnet h = MD5.new() h.update(hashstring) encryption_key = h.hexdigest() user_list_string = json.dumps(user_list) user_list_encrypted = crypt.urlsafe_encrypt( encryption_key, user_list_string) result['users'] = user_list_encrypted return result else: pecan.abort(400, _('Invalid request')) else: # Data for this subcloud requested # Build up and append a dictionary of the endpoints # sync status to the result. for subcloud, subcloud_status in db_api. \ subcloud_get_with_status(context, subcloud_id): subcloud_dict = db_api.subcloud_db_model_to_dict(subcloud) # may be empty subcloud_status entry, account for this if subcloud_status: subcloud_status_list.append( db_api.subcloud_endpoint_status_db_model_to_dict( subcloud_status)) endpoint_sync_dict = { consts.ENDPOINT_SYNC_STATUS: subcloud_status_list } subcloud_dict.update(endpoint_sync_dict) return subcloud_dict
def post(self, subcloud_ref=None): """Update or create sw update options. :param subcloud: name or id of subcloud (optional) """ # Note creating or updating subcloud specific options require # setting all options. context = restcomm.extract_context_from_environ() payload = eval(request.body) if not payload: pecan.abort(400, _('Body required')) if subcloud_ref == consts.DEFAULT_REGION_NAME: # update default options subcloud_name = consts.SW_UPDATE_DEFAULT_TITLE if db_api.sw_update_opts_default_get(context): # entry already in db, update it. try: sw_update_opts_ref = db_api.sw_update_opts_default_update( context, payload['storage-apply-type'], payload['compute-apply-type'], payload['max-parallel-computes'], payload['alarm-restriction-type'], payload['default-instance-action']) except Exception as e: LOG.exception(e) raise e else: # no entry in db, create one. try: sw_update_opts_ref = db_api.sw_update_opts_default_create( context, payload['storage-apply-type'], payload['compute-apply-type'], payload['max-parallel-computes'], payload['alarm-restriction-type'], payload['default-instance-action']) except Exception as e: LOG.exception(e) raise e else: # update subcloud options if subcloud_ref.isdigit(): # Look up subcloud as an ID try: subcloud = db_api.subcloud_get(context, subcloud_ref) except exceptions.SubcloudNotFound: pecan.abort(404, _('Subcloud not found')) subcloud_name = subcloud.name else: # Look up subcloud by name try: subcloud = db_api.subcloud_get_by_name( context, subcloud_ref) except exceptions.SubcloudNameNotFound: pecan.abort(404, _('Subcloud not found')) subcloud_name = subcloud_ref sw_update_opts = db_api.sw_update_opts_get(context, subcloud.id) if sw_update_opts is None: sw_update_opts_ref = db_api.sw_update_opts_create( context, subcloud.id, payload['storage-apply-type'], payload['compute-apply-type'], payload['max-parallel-computes'], payload['alarm-restriction-type'], payload['default-instance-action']) else: # a row is present in table, update sw_update_opts_ref = db_api.sw_update_opts_update( context, subcloud.id, payload['storage-apply-type'], payload['compute-apply-type'], payload['max-parallel-computes'], payload['alarm-restriction-type'], payload['default-instance-action']) return db_api.sw_update_opts_w_name_db_model_to_dict( sw_update_opts_ref, subcloud_name)
def add_subcloud(self, context, payload): """Add subcloud and notify orchestrators. :param context: request context object :param name: name of subcloud to add :param payload: subcloud configuration """ LOG.info("Adding subcloud %s." % payload['name']) try: subcloud = db_api.subcloud_get_by_name(context, payload['name']) except exceptions.SubcloudNameNotFound: pass else: raise exceptions.BadRequest( resource='subcloud', msg='Subcloud with that name already exists') # Subcloud is added with software version that matches system # controller. software_version = SW_VERSION try: subcloud = db_api.subcloud_create( context, payload['name'], payload.get('description'), payload.get('location'), software_version, payload['management-subnet'], payload['management-gateway-ip'], payload['management-start-ip'], payload['management-end-ip'], payload['systemcontroller-gateway-ip']) except Exception as e: LOG.exception(e) raise e # Populate the subcloud status table with all endpoints for endpoint in dcorch_consts.ENDPOINT_TYPES_LIST: db_api.subcloud_status_create(context, subcloud.id, endpoint) try: # Create a new route to this subcloud on the management interface # on both controllers. m_ks_client = KeystoneClient() subcloud_subnet = netaddr.IPNetwork(payload['management-subnet']) session = m_ks_client.endpoint_cache.get_session_from_token( context.auth_token, context.project) sysinv_client = SysinvClient(consts.DEFAULT_REGION_NAME, session) controllers = sysinv_client.get_controller_hosts() for controller in controllers: management_interface = sysinv_client.get_management_interface( controller.hostname) if management_interface is not None: sysinv_client.create_route( management_interface.uuid, str(subcloud_subnet.ip), subcloud_subnet.prefixlen, payload['systemcontroller-gateway-ip'], 1) # Create identity endpoints to this subcloud on the # management-start-ip of the subcloud which will be allocated # as the floating Management IP of the Subcloud if the # Address Pool is not shared. Incase the endpoint entry # is incorrect, or the management IP of the subcloud is changed # in the future, it will not go managed or will show up as # out of sync. To fix this use Openstack endpoint commands # on the SystemController to change the subcloud endpoint ks_service_id = None for service in m_ks_client.services_list: if service.type == dcorch_consts.ENDPOINT_TYPE_IDENTITY: ks_service_id = service.id break else: raise exceptions.BadRequest( resource='subcloud', msg='No Identity service found on SystemController') identity_endpoint_ip = payload['management-start-ip'] if netaddr.IPAddress(identity_endpoint_ip).version == 6: identity_endpoint_url = \ "http://[{}]:5000/v3".format(identity_endpoint_ip) else: identity_endpoint_url = \ "http://{}:5000/v3".format(identity_endpoint_ip) for iface in ['internal', 'admin']: m_ks_client.keystone_client.endpoints.create( ks_service_id, identity_endpoint_url, interface=iface, region=subcloud.name) # Inform orchestrator that subcloud has been added self.dcorch_rpc_client.add_subcloud(context, subcloud.name, subcloud.software_version) # Regenerate the addn_hosts_dc file self._create_addn_hosts_dc(context) return db_api.subcloud_db_model_to_dict(subcloud) except Exception as e: LOG.exception(e) # If we failed to create the subcloud, clean up anything we may # have done. self._delete_subcloud_routes(context, subcloud) db_api.subcloud_destroy(context, subcloud.id) raise e
def update_subcloud_endpoint_status( self, context, subcloud_name=None, endpoint_type=None, sync_status=consts.SYNC_STATUS_OUT_OF_SYNC, alarmable=True): """Update subcloud endpoint status :param context: request context object :param subcloud_name: name of subcloud to update :param endpoint_type: endpoint type to update :param sync_status: sync status to set """ subcloud = None if subcloud_name: try: subcloud = db_api.subcloud_get_by_name(context, subcloud_name) except Exception as e: LOG.exception(e) raise e # Only allow updating the sync status if managed and online. # This means if a subcloud is going offline or unmanaged, then # the sync status update must be done first. if (((subcloud.availability_status == consts.AVAILABILITY_ONLINE) and (subcloud.management_state == consts.MANAGEMENT_MANAGED)) or (sync_status != consts.SYNC_STATUS_IN_SYNC)): # update a single subcloud try: self._update_endpoint_status_for_subcloud( context, subcloud.id, endpoint_type, sync_status, alarmable) except Exception as e: LOG.exception(e) raise e else: LOG.info("Ignoring unmanaged/offline subcloud sync_status " "update for subcloud:%s endpoint:%s sync:%s" % (subcloud_name, endpoint_type, sync_status)) else: # update all subclouds for subcloud in db_api.subcloud_get_all(context): if (((subcloud.availability_status == consts.AVAILABILITY_ONLINE) and (subcloud.management_state == consts.MANAGEMENT_MANAGED)) or (sync_status != consts.SYNC_STATUS_IN_SYNC)): try: self._update_endpoint_status_for_subcloud( context, subcloud.id, endpoint_type, sync_status, alarmable) except Exception as e: LOG.exception(e) raise e else: LOG.info("Ignoring unmanaged/offline subcloud sync_status " "update for subcloud:%s endpoint:%s sync:%s" % (subcloud.name, endpoint_type, sync_status))
def add_subcloud(self, context, payload): """Add subcloud and notify orchestrators. :param context: request context object :param name: name of subcloud to add :param payload: subcloud configuration """ LOG.info("Adding subcloud %s." % payload['name']) try: subcloud = db_api.subcloud_get_by_name(context, payload['name']) except exceptions.SubcloudNameNotFound: pass else: raise exceptions.BadRequest( resource='subcloud', msg='Subcloud with that name already exists') # Subcloud is added with software version that matches system # controller. software_version = SW_VERSION try: subcloud = db_api.subcloud_create( context, payload['name'], payload.get('description'), payload.get('location'), software_version, payload['management-subnet'], payload['management-gateway-ip'], payload['management-start-ip'], payload['management-end-ip'], payload['systemcontroller-gateway-ip']) except Exception as e: LOG.exception(e) raise e # Populate the subcloud status table with all endpoints for endpoint in dcorch_consts.ENDPOINT_TYPES_LIST: db_api.subcloud_status_create(context, subcloud.id, endpoint) try: # Create a new route to this subcloud on the management interface # on both controllers. subcloud_subnet = netaddr.IPNetwork(payload['management-subnet']) session = endpoint_cache.EndpointCache().get_session_from_token( context.auth_token, context.project) sysinv_client = SysinvClient(consts.DEFAULT_REGION_NAME, session) controllers = sysinv_client.get_controller_hosts() for controller in controllers: management_interface = sysinv_client.get_management_interface( controller.hostname) if management_interface is not None: sysinv_client.create_route( management_interface.uuid, str(subcloud_subnet.ip), subcloud_subnet.prefixlen, payload['systemcontroller-gateway-ip'], 1) # Inform orchestrator that subcloud has been added self.dcorch_rpc_client.add_subcloud(context, subcloud.name, subcloud.software_version) # Regenerate the addn_hosts_dc file self._create_addn_hosts_dc(context) return db_api.subcloud_db_model_to_dict(subcloud) except Exception as e: LOG.exception(e) # If we failed to create the subcloud, clean up anything we may # have done. self._delete_subcloud_routes(context, subcloud) db_api.subcloud_destroy(context, subcloud.id) raise e