def check_cluster_member(message, bus): """ Checks is a member is part of the 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: host = message['params']['host'] name = message['params']['name'] cluster = bus.storage.get_cluster(name) if host in cluster.hostset: # Return back the host in a list return create_jsonrpc_response(message['id'], [host]) else: return create_jsonrpc_response( message['id'], error='The requested host is not part of the cluster.', error_code=JSONRPC_ERRORS['NOT_FOUND']) except Exception as error: return create_jsonrpc_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: name = message['params']['name'] bus.storage.get_cluster(name) LOGGER.debug('Creation of already exisiting cluster %s requested.', name) except Exception as error: LOGGER.debug('Brand new cluster being created.') network_name = message['params'].get('network') if network_name: # Verify the networks existence try: bus.storage.get_network(network_name) except Exception as error: # Default if the network doesn't exist message['params']['network'] = C.DEFAULT_CLUSTER_NETWORK_JSON[ 'name'] # noqa try: cluster = bus.storage.save(models.Cluster.new(**message['params'])) return create_jsonrpc_response(message['id'], cluster.to_dict_safe()) except models.ValidationError as error: return create_jsonrpc_error(message, error, JSONRPC_ERRORS['INVALID_REQUEST'])
def create_cluster_deploy(message, bus): """ Creates a new cluster deployment. :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: cluster_deploy = models.ClusterDeploy.new( name=message['params'].get('name'), version=message['params'].get('version')) cluster_deploy._validate() result = bus.request( 'jobs.clusterexec.deploy', params=[cluster_deploy.name, cluster_deploy.version]) return create_jsonrpc_response(message['id'], result['result']) except models.ValidationError as error: LOGGER.info('Invalid data provided. "%s"', error) LOGGER.debug('Data="%s"', message['params']) return create_jsonrpc_error(message, error, JSONRPC_ERRORS['INVALID_REQUEST']) except Exception as error: LOGGER.debug('Error creating ClusterDeploy: %s: %s', type(error), error) return create_jsonrpc_error(message, error, JSONRPC_ERRORS['INTERNAL_ERROR'])
def delete_container_manager(message, bus): """ Deletes an exisiting ContainerManagerConfig. :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: name = message['params']['name'] LOGGER.debug( 'Attempting to delete ContainerManagerConfig "{}"'.format(name)) bus.storage.delete(models.ContainerManagerConfig.new(name=name)) return create_jsonrpc_response(message['id'], []) except _bus.StorageLookupError as error: return create_jsonrpc_error(message, error, JSONRPC_ERRORS['NOT_FOUND']) except Exception as error: LOGGER.debug('Error deleting ContainerManagerConfig: {}: {}'.format( type(error), error)) return create_jsonrpc_error(message, error, JSONRPC_ERRORS['INTERNAL_ERROR'])
def get_cluster_operation(model_cls, message, bus): """ Gets a specific operation based on the model_cls. :param model_cls: The model class to use. :type model_cls: class :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: name = message['params']['name'] model = bus.storage.get(model_cls.new(name=name)) model._validate() return create_jsonrpc_response(message['id'], model.to_dict_safe()) except models.ValidationError as error: LOGGER.info('Invalid data retrieved. "%s"', error) LOGGER.debug('Data="%s"', message['params']) return create_jsonrpc_error(message, error, JSONRPC_ERRORS['INVALID_REQUEST']) except _bus.StorageLookupError as error: return create_jsonrpc_error(message, error, JSONRPC_ERRORS['NOT_FOUND']) except Exception as error: LOGGER.debug('Error getting %s: %s: %s', model_cls.__name__, type(error), error) return create_jsonrpc_error(message, error, JSONRPC_ERRORS['INTERNAL_ERROR'])
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 """ try: name = message['params']['name'] cluster = bus.storage.get_cluster(name) LOGGER.debug('Cluster found: {}'.format(cluster.name)) LOGGER.debug('Returning: {}'.format(cluster.hostset)) return create_jsonrpc_response( message['id'], result=cluster.hostset) except _bus.StorageLookupError as error: return create_jsonrpc_error( message, error, JSONRPC_ERRORS['NOT_FOUND']) except Exception as error: LOGGER.debug('Error listing cluster: {}: {}'.format( type(error), error)) return create_jsonrpc_error( message, error, JSONRPC_ERRORS['INTERNAL_ERROR'])
def get_cluster_deploy(message, bus): """ Gets a specific deployment. :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: name = message['params']['name'] cluster_deploy = bus.storage.get(models.ClusterDeploy.new(name=name)) cluster_deploy._validate() return create_jsonrpc_response(message['id'], cluster_deploy.to_dict_safe()) except models.ValidationError as error: LOGGER.info('Invalid data retrieved. "%s"', error) LOGGER.debug('Data="%s"', message['params']) return create_jsonrpc_error(message, error, JSONRPC_ERRORS['INVALID_REQUEST']) except _bus.StorageLookupError as error: return create_jsonrpc_error(message, error, JSONRPC_ERRORS['NOT_FOUND']) except Exception as error: return create_jsonrpc_error(message, error, JSONRPC_ERRORS['INTERNAL_ERROR'])
def delete_cluster(message, bus): """ Deletes an existing 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: name = message['params']['name'] LOGGER.debug('Attempting to delete cluster "%s"', name) cluster = bus.storage.get_cluster(name) if cluster.container_manager: params = [cluster.container_manager] bus.request('container.remove_all_nodes', params=params) bus.storage.delete(cluster) return create_jsonrpc_response(message['id'], []) except _bus.StorageLookupError as error: return create_jsonrpc_error(message, error, JSONRPC_ERRORS['NOT_FOUND']) except Exception as error: LOGGER.debug('Error deleting cluster: %s: %s'.format( type(error), error)) return create_jsonrpc_error(message, error, JSONRPC_ERRORS['INTERNAL_ERROR'])
def delete_cluster_member(message, bus): """ Deletes a member from the 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: host = message['params']['host'] name = message['params']['name'] cluster = bus.storage.get_cluster(name) if host in cluster.hostset: # 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. idx = cluster.hostset.index(host) cluster.hostset.pop(idx) bus.storage.save(cluster) # Remove from container manager (if applicable) if cluster.container_manager: params = [cluster.container_manager, host] bus.request('container.remove_node', params=params) return create_jsonrpc_response(message['id'], []) except Exception as error: return create_jsonrpc_error(message, error, JSONRPC_ERRORS['NOT_FOUND'])
def test_list_hosts(self): """ Verify list_hosts responds with the right information. """ bus = mock.MagicMock() bus.storage.list.return_value = Hosts.new(hosts=[HOST]) self.assertEquals(create_jsonrpc_response(ID, [HOST.to_dict_safe()]), hosts.list_hosts.handler(NO_PARAMS_REQUEST, bus))
def test_get_host(self): """ Verify get_host responds with the right information. """ bus = mock.MagicMock() bus.storage.get_host.return_value = HOST self.assertEquals(create_jsonrpc_response(ID, HOST.to_dict_safe()), hosts.get_host.handler(SIMPLE_HOST_REQUEST, bus))
def test_list_clusters(self): """ Verify list_clusters responds with the right information. """ bus = mock.MagicMock() bus.storage.list.return_value = Clusters.new(clusters=[CLUSTER]) self.assertEquals( create_jsonrpc_response(ID, [CLUSTER.name]), clusters.list_clusters.handler(NO_PARAMS_REQUEST, bus))
def test_create_jsonrpc_response_with_result(self): """ Verify create_jsonrpc_response creates the proper result jsonrpc structure. """ response = handlers.create_jsonrpc_response( UID, result={'test': 'data'}) self.assertEquals('2.0', response['jsonrpc']) self.assertEquals(UID, response['id']) self.assertEquals({'test': 'data'}, response['result'])
def test_list_networks(self): """ Verify list_networks responds with the right information. """ bus = mock.MagicMock() bus.storage.list.return_value = Networks.new(networks=[NETWORK]) self.assertEquals( create_jsonrpc_response(ID, ['test']), networks.list_networks.handler(NO_PARAMS_REQUEST, bus))
def test_get_network(self): """ Verify get_network responds with the right information. """ bus = mock.MagicMock() bus.storage.get_network.return_value = NETWORK self.assertEquals( create_jsonrpc_response(ID, NETWORK.to_dict()), networks.get_network.handler(SIMPLE_NETWORK_REQUEST, bus))
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: name = message['params']['name'] LOGGER.debug('create_network params: {}'.format(message['params'])) # Check to see if we already have a network with that name input_network = models.Network.new(**message['params']) saved_network = bus.storage.get(input_network) LOGGER.debug( 'Creation of already exisiting network "{0}" requested.'.format( name)) # If they are the same thing then go ahead and return success if saved_network.to_dict() == input_network.to_dict(): return create_jsonrpc_response( message['id'], saved_network.to_dict_safe()) # Otherwise error with a CONFLICT return create_jsonrpc_error( message, 'A network with that name already exists.', JSONRPC_ERRORS['CONFLICT']) except _bus.StorageLookupError as error: LOGGER.info('Attempting to create new network: "{}"'.format( message['params'])) # Create the new network try: input_network = models.Network.new(**message['params']) saved_network = bus.storage.save(input_network) return create_jsonrpc_response( message['id'], saved_network.to_dict_safe()) except models.ValidationError as error: return create_jsonrpc_error( message, error, JSONRPC_ERRORS['INVALID_REQUEST'])
def test_get_container_manager(self): """ Verify get_container_manager responds with the right information. """ bus = mock.MagicMock() bus.storage.get.return_value = CONTAINER_MANAGER_CONFIG self.assertEquals( create_jsonrpc_response(ID, CONTAINER_MANAGER_CONFIG.to_dict()), container_managers.get_container_manager.handler( SIMPLE_CONTAINER_MANAGER_CONFIG_REQUEST, bus))
def test_list_cluster_members(self): """ Verify that list_cluster_members returns proper information. """ bus = mock.MagicMock() bus.storage.get_cluster.return_value = Cluster.new( name='test', hostset=['127.0.0.1']) self.assertEquals( create_jsonrpc_response(ID, ['127.0.0.1']), clusters.list_cluster_members.handler(SIMPLE_CLUSTER_REQUEST, bus))
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: address = message['params']['address'] host = bus.storage.get_host(address) container_manager = {} # Find the host's container manager status (if applicable) container = bus.storage.list(models.Clusters) for cluster in container.clusters: if address in cluster.hostset: if cluster.container_manager: try: params = [cluster.container_manager, address] container_manager = bus.request( 'container.get_node_status', params=params) except _bus.ContainerManagerError as error: # If we fail to get the container manager's # status for the host, leave that part of the # status structure empty. pass # A host can only be part of one cluster so break the loop. break status = models.HostStatus.new( host={ 'last_check': host.last_check, 'status': host.status, }, container_manager=container_manager, # TODO: Update when we add other types. type='host_only') LOGGER.debug('Status for host "{0}": "{1}"'.format( host.address, status.to_json_safe())) return create_jsonrpc_response(message['id'], status.to_dict_safe()) except _bus.RemoteProcedureCallError as error: return create_jsonrpc_error(message, error, JSONRPC_ERRORS['NOT_FOUND']) except Exception as error: LOGGER.debug('Host Status exception caught for {0}: {1}:{2}'.format( address, type(error), error)) return create_jsonrpc_error(message, error, JSONRPC_ERRORS['INTERNAL_ERROR'])
def test_list_container_managers(self): """ Verify list_container_managers responds with the right information. """ bus = mock.MagicMock() bus.storage.list.return_value = ContainerManagerConfigs.new( container_managers=[CONTAINER_MANAGER_CONFIG]) self.assertEquals( create_jsonrpc_response(ID, ['test']), container_managers.list_container_managers.handler( NO_PARAMS_REQUEST, bus))
def test_get_host_creds(self): """ Verify get_hostcreds responds with the right information. """ bus = mock.MagicMock() bus.storage.get_host.return_value = HOST self.assertEquals( create_jsonrpc_response(ID, { 'ssh_priv_key': '', 'remote_user': '******' }), hosts.get_hostcreds.handler(SIMPLE_HOST_REQUEST, bus))
def test_create_cluster(self): """ Verify create_cluster saves new clusters. """ bus = mock.MagicMock() bus.storage.get_cluster.side_effect = Exception bus.storage.save.return_value = CLUSTER self.assertEquals( create_jsonrpc_response(ID, CLUSTER.to_dict_safe()), clusters.create_cluster.handler(SIMPLE_CLUSTER_REQUEST, bus))
def test_create_network(self): """ Verify create_network can create a new network. """ bus = mock.MagicMock() # Network doesn't yet exist bus.storage.get.side_effect = _bus.StorageLookupError('test', NETWORK) # Creation response bus.storage.save.return_value = NETWORK self.assertEquals( create_jsonrpc_response(ID, NETWORK.to_dict()), networks.create_network.handler(SIMPLE_NETWORK_REQUEST, bus))
def test_create_network_idempotent(self): """ Verify create_network acts idempotent. """ bus = mock.MagicMock() # Network exists bus.storage.get.return_value = NETWORK # Creation response bus.storage.save.return_value = NETWORK self.assertEquals( create_jsonrpc_response(ID, NETWORK.to_dict()), networks.create_network.handler(SIMPLE_NETWORK_REQUEST, bus))
def test_create_host_with_the_same_existing_host(self): """ Verify create_host succeeds when a new host matches an existing one. """ bus = mock.MagicMock() # Existing host bus.storage.get_host.return_value = HOST # Result from save bus.storage.save.return_value = HOST self.assertEquals(create_jsonrpc_response(ID, HOST.to_dict_safe()), hosts.create_host.handler(SIMPLE_HOST_REQUEST, bus))
def test_create_container_manager_idempotent(self): """ Verify create_container_manager acts idempotent. """ bus = mock.MagicMock() # ContainerManagerConfig exists bus.storage.get.return_value = CONTAINER_MANAGER_CONFIG # Creation response bus.storage.save.return_value = CONTAINER_MANAGER_CONFIG self.assertEquals( create_jsonrpc_response(ID, CONTAINER_MANAGER_CONFIG.to_dict()), container_managers.create_container_manager.handler( SIMPLE_CONTAINER_MANAGER_CONFIG_REQUEST, bus))
def test_create_host(self): """ Verify create_host saves new hosts. """ bus = mock.MagicMock() # Host doesn't exist yet bus.storage.get_host.side_effect = _bus.RemoteProcedureCallError( 'test') # Result from save bus.storage.save.return_value = HOST self.assertEquals(create_jsonrpc_response(ID, HOST.to_dict_safe()), hosts.create_host.handler(SIMPLE_HOST_REQUEST, bus))
def test_check_cluster_member_with_valid_member(self): """ Verify that check_cluster_member returns proper data when a valid member is requested. """ bus = mock.MagicMock() cluster = Cluster.new( name='test', hostset=['127.0.0.1']) bus.storage.get_cluster.return_value = cluster self.assertEquals( create_jsonrpc_response(ID, ['127.0.0.1']), clusters.check_cluster_member.handler(CHECK_CLUSTER_REQUEST, bus))
def add_cluster_member(message, bus): """ Adds a member to the 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: address = message['params']['host'] name = message['params']['name'] except KeyError as error: return create_jsonrpc_error( message, error, JSONRPC_ERRORS['BAD_REQUEST']) try: host = bus.storage.get_host(address) cluster = bus.storage.get_cluster(name) except _bus.StorageLookupError as error: return create_jsonrpc_error( message, error, JSONRPC_ERRORS['NOT_FOUND']) if host.address not in cluster.hostset: # FIXME: Need more input validation. # - 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. if host_suitable_for_cluster(host): cluster.hostset.append(host.address) bus.storage.save(cluster) # Register new host with the cluster's container manager # (if applicable), and update its status. update_new_cluster_member_status(bus, cluster, host) else: msg = ( 'Host {} (status: {}) not ready to join cluster ' '"{}"'.format(host.address, host.status, cluster.name)) LOGGER.error(msg) return create_jsonrpc_error( message, msg, JSONRPC_ERRORS['METHOD_NOT_ALLOWED']) # Return back the host in a list return create_jsonrpc_response(message['id'], [host.address])
def test_create_container_manager(self): """ Verify create_container_manager can create a new ContainerManagerConfig. """ bus = mock.MagicMock() # ContainerManagerConfig doesn't yet exist bus.storage.get.side_effect = _bus.StorageLookupError( 'test', CONTAINER_MANAGER_CONFIG) # Creation response bus.storage.save.return_value = CONTAINER_MANAGER_CONFIG self.assertEquals( create_jsonrpc_response(ID, CONTAINER_MANAGER_CONFIG.to_dict()), container_managers.create_container_manager.handler( SIMPLE_CONTAINER_MANAGER_CONFIG_REQUEST, bus))