def delete_host(message, bus): """ Deletes an existing host. :param message: jsonrpc message structure. :type message: dict :param bus: Bus instance. :type bus: commissaire_http.bus.Bus :returns: A jsonrpc structure. :rtype: dict """ try: address = message['params']['address'] LOGGER.debug('Attempting to delete host "{}"'.format(address)) bus.request('storage.delete', params=['Host', {'address': address}]) # TODO: kick off service job to remove the host? # Remove from a cluster for cluster in bus.request('storage.list', params=['Clusters', True])['result']: if address in cluster['hostset']: LOGGER.info('Removing host "{}" from cluster "{}"'.format( address, cluster['name'])) cluster['hostset'].pop(cluster['hostset'].index(address)) bus.request('storage.save', params=['Cluster', cluster]) # A host can only be part of one cluster so break the loop break return create_response(message['id'], []) except _bus.RemoteProcedureCallError as error: LOGGER.debug('Error deleting host: {}: {}'.format(type(error), error)) return return_error(message, error, JSONRPC_ERRORS['NOT_FOUND']) except Exception as error: LOGGER.debug('Error deleting host: {}: {}'.format(type(error), error)) return return_error(message, error, JSONRPC_ERRORS['INTERNAL_ERROR'])
def delete_network(message, bus): """ Deletes an exisiting network. :param message: jsonrpc message structure. :type message: dict :param bus: Bus instance. :type bus: commissaire_http.bus.Bus :returns: A jsonrpc structure. :rtype: dict """ try: LOGGER.debug('Attempting to delete network "{}"'.format( message['params']['name'])) bus.request('storage.delete', params=['Network', { 'name': message['params']['name'] }]) return create_response(message['id'], []) except _bus.RemoteProcedureCallError as error: LOGGER.debug('Error deleting network: {}: {}'.format( type(error), error)) return return_error(message, error, JSONRPC_ERRORS['NOT_FOUND']) except Exception as error: LOGGER.debug('Error deleting network: {}: {}'.format( type(error), error)) return return_error(message, error, JSONRPC_ERRORS['INTERNAL_ERROR'])
def create_network(message, bus): """ Creates a new network. :param message: jsonrpc message structure. :type message: dict :param bus: Bus instance. :type bus: commissaire_http.bus.Bus :returns: A jsonrpc structure. :rtype: dict """ try: LOGGER.debug('create_network params: {}'.format(message['params'])) # Check to see if we already have a network with that name network = bus.request( 'storage.get', params=['Network', { 'name': message['params']['name'] }]) LOGGER.debug( 'Creation of already exisiting network "{0}" requested.'.format( message['params']['name'])) # If they are the same thing then go ahead and return success if models.Network.new( **network['result']).to_dict() == models.Network.new( **message['params']).to_dict(): return create_response(message['id'], network['result']) # Otherwise error with a CONFLICT return return_error(message, 'A network with that name already exists.', JSONRPC_ERRORS['CONFLICT']) except _bus.RemoteProcedureCallError as error: LOGGER.debug('Error getting network: {}: {}'.format( type(error), error)) LOGGER.info('Attempting to create new network: "{}"'.format( message['params'])) # Create the new network try: network = models.Network.new(**message['params']) network._validate() response = bus.request('storage.save', params=['Network', network.to_dict()]) return create_response(message['id'], response['result']) except models.ValidationError as error: return return_error(message, error, JSONRPC_ERRORS['INVALID_REQUEST'])
def update_cluster_members(message, bus): """ Updates the list of members in a cluster. :param message: jsonrpc message structure. :type message: dict :param bus: Bus instance. :type bus: commissaire_http.bus.Bus :returns: A jsonrpc structure. :rtype: dict """ try: old_hosts = set(message['params']['old']) # Ensures no duplicates new_hosts = set(message['params']['new']) # Ensures no duplicates LOGGER.debug('old_hosts="{}", new_hosts="{}"'.format( old_hosts, new_hosts)) except Exception as error: return return_error(message, error, JSONRPC_ERRORS['BAD_REQUEST']) try: cluster = bus.request( 'storage.get', params=['Cluster', { 'name': message['params']['name'] }, True]) except Exception as error: return return_error(message, error, JSONRPC_ERRORS['NOT_FOUND']) if old_hosts != set(cluster['result']['hostset']): msg = 'Conflict setting hosts for cluster {0}'.format( cluster['result']['name']) LOGGER.error(msg) return return_error(message, msg, JSONRPC_ERRORS['CONFLICT']) # FIXME: Need input validation. For each new host, # - Does the host exist at /commissaire/hosts/{IP}? # - Does the host already belong to another cluster? # FIXME: Should guard against races here, since we're fetching # the cluster record and writing it back with some parts # unmodified. Use either locking or a conditional write # with the etcd 'modifiedIndex'. Deferring for now. cluster['result']['hostset'] = list(new_hosts) cluster = models.Cluster.new(**cluster['result']) cluster._validate() response = bus.request('storage.save', params=['Cluster', cluster.to_dict()]) return create_response(message['id'], response['result'])
def get_hostcreds(message, bus): """ Gets credentials for a host. :param message: jsonrpc message structure. :type message: dict :param bus: Bus instance. :type bus: commissaire_http.bus.Bus :returns: A jsonrpc structure. :rtype: dict """ try: host_response = bus.request( 'storage.get', params=['Host', { 'address': message['params']['address'] }, True]) creds = { 'remote_user': host_response['result']['remote_user'], 'ssh_priv_key': host_response['result']['ssh_priv_key'] } return create_response(message['id'], creds) except _bus.RemoteProcedureCallError as error: LOGGER.debug('Client requested a non-existant host: "{}"'.format( message['params']['address'])) return return_error(message, error, JSONRPC_ERRORS['NOT_FOUND'])
def test_return_error(self): """ Ensure return_error returns a proper error structure. """ result = handlers.return_error({'id': UID}, Exception('test'), 1) self.assertEquals(1, result['error']['code']) self.assertEquals('test', result['error']['message']) self.assertEquals(str(Exception), result['error']['data']['exception'])
def get_host_status(message, bus): """ Gets the status of an exisiting host. :param message: jsonrpc message structure. :type message: dict :param bus: Bus instance. :type bus: commissaire_http.bus.Bus :returns: A jsonrpc structure. :rtype: dict """ try: host_response = bus.request( 'storage.get', params=['Host', { 'address': message['params']['address'] }]) host = models.Host.new(**host_response['result']) status = models.HostStatus.new( host={ 'last_check': host.last_check, 'status': host.status, }, # TODO: Update when we add other types. type=C.CLUSTER_TYPE_HOST) LOGGER.debug('Status for host "{0}": "{1}"'.format( host.address, status.to_json())) return create_response(message['id'], status.to_dict()) except _bus.RemoteProcedureCallError as error: LOGGER.warn('Could not retrieve host "{}". {}: {}'.format( message['params']['address'], type(error), error)) return return_error(message, error, JSONRPC_ERRORS['NOT_FOUND']) except Exception as error: LOGGER.debug('Host Status exception caught for {0}: {1}:{2}'.format( host.address, type(error), error)) return return_error(message, error, JSONRPC_ERRORS['INTERNAL_ERROR'])
def create_cluster(message, bus): """ Creates a new cluster. :param message: jsonrpc message structure. :type message: dict :param bus: Bus instance. :type bus: commissaire_http.bus.Bus :returns: A jsonrpc structure. :rtype: dict """ try: bus.request('storage.get', params=['Cluster', { 'name': message['params']['name'] }]) LOGGER.debug( 'Creation of already exisiting cluster {0} requested.'.format( message['params']['name'])) except Exception as error: LOGGER.debug('Brand new cluster being created.') if message['params'].get('network'): # Verify the networks existence try: bus.request( 'storage.get', params=['Network', { 'name': message['params']['network'] }]) except Exception as error: # Default if the network doesn't exist message['params']['network'] = C.DEFAULT_CLUSTER_NETWORK_JSON[ 'name'] # noqa try: cluster = models.Cluster.new(**message['params']) cluster._validate() response = bus.request('storage.save', params=['Cluster', cluster.to_dict()]) return create_response(message['id'], response['result']) except models.ValidationError as error: return return_error(message, error, JSONRPC_ERRORS['INVALID_REQUEST'])
def list_cluster_members(message, bus): """ Lists hosts in a cluster. :param message: jsonrpc message structure. :type message: dict :param bus: Bus instance. :type bus: commissaire_http.bus.Bus :returns: A jsonrpc structure. :rtype: dict """ response = None try: cluster = bus.request('storage.get', params=[ 'Cluster', {'name': message['params']['name']}, True]) LOGGER.debug('Cluster found: {}'.format(cluster['result']['name'])) LOGGER.debug('Returning: {}'.format(response)) return create_response( message['id'], result=cluster['result']['hostset']) except Exception as error: return return_error(message, error, JSONRPC_ERRORS['NOT_FOUND'])
def get_network(message, bus): """ Gets a specific network. :param message: jsonrpc message structure. :type message: dict :param bus: Bus instance. :type bus: commissaire_http.bus.Bus :returns: A jsonrpc structure. :rtype: dict """ try: network_response = bus.request( 'storage.get', params=['Network', { 'name': message['params']['name'] }, True]) network = models.Network.new(**network_response['result']) return create_response(message['id'], network.to_dict()) except _bus.RemoteProcedureCallError as error: return return_error(message, error, JSONRPC_ERRORS['NOT_FOUND'])
def create_host(message, bus): """ Creates a new host. :param message: jsonrpc message structure. :type message: dict :param bus: Bus instance. :type bus: commissaire_http.bus.Bus :returns: A jsonrpc structure. :rtype: dict """ LOGGER.debug('create_host params: "{}"'.format(message['params'])) try: address = message['params']['address'] except KeyError: return return_error( message, '"address" must be given in the url or in the PUT body', JSONRPC_ERRORS['INVALID_PARAMETERS']) try: host = bus.request('storage.get', params=[ 'Host', {'address': address}, True]) LOGGER.debug('Host "{}" already exisits.'.format(address)) # Verify the keys match if (host['result']['ssh_priv_key'] != message['params'].get('ssh_priv_key', '')): return return_error( message, 'Host already exists', JSONRPC_ERRORS['CONFLICT']) # Verify the cluster exists and it's in the cluster if message['params'].get('cluster'): cluster = _does_cluster_exist(bus, message['params']['cluster']) if not cluster: return return_error( message, 'Cluster does not exist', JSONRPC_ERRORS['INVALID_PARAMETERS']) # Verify the host is in the cluster if address not in cluster.hostset: LOGGER.debug('Host "{}" is not in cluster "{}"'.format( address, message['params']['cluster'])) return return_error( message, 'Host not in cluster', JSONRPC_ERRORS['CONFLICT']) # Return out now. No more processing needed. return create_response( message['id'], models.Host.new(**host['result']).to_dict()) except _bus.RemoteProcedureCallError as error: LOGGER.debug('Brand new host "{}" being created.'.format( message['params']['address'])) if message['params'].get('cluster'): # Verify the cluster existence and add the host to it cluster_name = message['params']['cluster'] cluster = _does_cluster_exist(bus, message['params']['cluster']) if not cluster: LOGGER.warn( 'create_host could not find cluster "{}" for the creation ' 'of new host "{}"'.format(cluster_name, address)) return return_error( message, 'Cluster does not exist', JSONRPC_ERRORS['INVALID_PARAMETERS']) LOGGER.debug('Found cluster. Data: "{}"'.format(cluster)) if address not in cluster.hostset: cluster.hostset.append(address) bus.request('storage.save', params=[ 'Cluster', cluster.to_dict(True)]) try: # TODO: pass this off to the bootstrap process host = models.Host.new(**message['params']) host._validate() bus.request( 'storage.save', params=[ 'Host', host.to_dict(True)]) return create_response(message['id'], host.to_dict()) except models.ValidationError as error: return return_error(message, error, JSONRPC_ERRORS['INVALID_REQUEST'])