def delete(self, host_id, subkey): """ Delete one attribute in configuration of the specified cluster host :param host_id: the unique identifier of the host :param subkey: the attribute name in configuration """ available_delete_keys = ['ip', 'roles'] with database.session() as session: host = session.query(ModelClusterHost).filter_by(id=host_id)\ .first() if not host: error_msg = "The host id=%s does not exist!" % host_id return errors.handle_not_exist( errors.ObjectDoesNotExist(error_msg)) if subkey not in available_delete_keys: error_msg = "subkey %s is not supported!" % subkey return errors.handle_invalid_usage( errors.UserInvalidUsage(error_msg)) if not host.mutable: error_msg = "The host 'id=%s' is not mutable!" % host_id return errors.handle_invalid_usage( errors.UserInvalidUsage(error_msg)) config = json.loads(host.config_data) # Set the subkey's value to "" util.update_dict_value(subkey, "", config) host.config = config return util.make_json_response(200, {"status": "OK"})
def put(self, cluster_id, resource): """ Update the resource information of the specified cluster in database :param cluster_id: the unique identifier of the cluster :param resource: resource name(security, networking, partition) """ resources = { self.SECURITY: { 'validator': 'is_valid_security_config', 'column': 'security_config' }, self.NETWORKING: { 'validator': 'is_valid_networking_config', 'column': 'networking_config' }, self.PARTITION: { 'validator': 'is_valid_partition_config', 'column': 'partition_config' }, } request_data = json.loads(request.data) with database.session() as session: cluster = session.query(ModelCluster).filter_by(id=cluster_id)\ .first() if not cluster: error_msg = 'You are trying to update a non-existing cluster!' return errors.handle_not_exist( errors.ObjectDoesNotExist(error_msg)) if resource not in request_data: error_msg = "Invalid resource name '%s'" % resource return errors.handle_invalid_usage( errors.UserInvalidUsage(error_msg)) value = request_data[resource] if resource not in resources.keys(): error_msg = "Invalid resource name '%s'" % resource return errors.handle_invalid_usage( errors.UserInvalidUsage(error_msg)) validate_func = resources[resource]['validator'] module = globals()['util'] is_valid, msg = getattr(module, validate_func)(value) if is_valid: column = resources[resource]['column'] session.query(ModelCluster).filter_by(id=cluster_id)\ .update({column: json.dumps(value)}) else: return errors.handle_mssing_input( errors.InputMissingError(msg)) return util.make_json_response(200, {"status": "OK"})
def post(self): """ Insert switch IP and the credential to db. Invoke a task to poll switch at the same time. :param ip: switch IP address :param credential: a dict for accessing the switch """ ip_addr = None credential = None logging.debug('post switch request from curl is %s', request.data) json_data = json.loads(request.data) ip_addr = json_data['switch']['ip'] credential = json_data['switch']['credential'] logging.info('post switch ip_addr=%s credential=%s(%s)', ip_addr, credential, type(credential)) if not util.is_valid_ip(ip_addr): error_msg = "Invalid IP address format!" return errors.handle_invalid_usage( errors.UserInvalidUsage(error_msg)) new_switch = {} with database.session() as session: switch = session.query(ModelSwitch).filter_by(ip=ip_addr).first() logging.info('switch for ip %s: %s', ip_addr, switch) if switch: error_msg = "IP address '%s' already exists" % ip_addr value = {'failedSwitch': switch.id} return errors.handle_duplicate_object( errors.ObjectDuplicateError(error_msg), value) switch = ModelSwitch(ip=ip_addr) switch.credential = credential session.add(switch) session.flush() new_switch['id'] = switch.id new_switch['ip'] = switch.ip new_switch['state'] = switch.state link = { 'rel': 'self', 'href': '/'.join((self.ENDPOINT, str(switch.id))) } new_switch['link'] = link celery.send_task("compass.tasks.pollswitch", (ip_addr, )) logging.info('new switch added: %s', new_switch) return util.make_json_response(202, { "status": "accepted", "switch": new_switch })
def get(self, cluster_id, resource=None): """ Lists details of the specified cluster if resource is not specified. Otherwise, lists details of the resource of this cluster :param cluster_id: the unique identifier of the cluster :param resource: the resource name(security, networking, partition) """ cluster_resp = {} resp = {} with database.session() as session: cluster = session.query(ModelCluster)\ .filter_by(id=cluster_id)\ .first() logging.debug('cluster is %s', cluster) if not cluster: error_msg = 'Cannot found the cluster with id=%s' % cluster_id return errors.handle_not_exist( errors.ObjectDoesNotExist(error_msg)) if resource: # List resource details if resource == self.SECURITY: cluster_resp = cluster.security elif resource == self.NETWORKING: cluster_resp = cluster.networking elif resource == self.PARTITION: cluster_resp = cluster.partition else: error_msg = "Invalid resource name '%s'!" % resource return errors.handle_invalid_usage( errors.UserInvalidUsage(error_msg)) resp = {"status": "OK", resource: cluster_resp} else: cluster_resp['clusterName'] = cluster.name cluster_resp['link'] = { 'rel': 'self', 'href': '/'.join((self.ENDPOINT, str(cluster.id))) } cluster_resp['id'] = cluster.id resp = {"status": "OK", "cluster": cluster_resp} logging.info('get cluster result is %s', cluster_resp) return util.make_json_response(200, resp)
def put(self, host_id): """ Update configuration of the specified cluster host :param host_id: the unique identifier of the host """ with database.session() as session: host = session.query(ModelClusterHost).filter_by(id=host_id)\ .first() if not host: error_msg = "The host id=%s does not exist!" % host_id return errors.handle_not_exist( errors.ObjectDoesNotExist(error_msg)) logging.debug("cluster config put request.data %s", request.data) request_data = json.loads(request.data) if not request_data: error_msg = "request data is %s" % request_data return errors.handle_mssing_input( errors.InputMissingError(error_msg)) if not host.mutable: error_msg = "The host 'id=%s' is not mutable!" % host_id return errors.handle_invalid_usage( errors.UserInvalidUsage(error_msg)) #Valid if keywords in request_data are all correct if 'hostname' in request_data: session.query(ModelClusterHost).filter_by(id=host_id)\ .update({"hostname": request_data['hostname']}) del request_data['hostname'] try: util.valid_host_config(request_data) except errors.UserInvalidUsage as exc: return errors.handle_invalid_usage(exc) host.config = request_data return util.make_json_response(200, {"status": "OK"})
def execute_cluster_action(cluster_id): """Execute the specified action to the cluster. :param cluster_id: the unique identifier of the cluster :param addHosts: the action of adding excepted hosts to the cluster :param removeHosts: the action of removing expected hosts from the cluster :param replaceAllHosts: the action of removing all existing hosts in cluster and add new hosts :param deploy: the action of starting to deploy """ def _add_hosts(cluster_id, hosts): """Add cluster host(s) to the cluster by cluster_id""" cluseter_hosts = [] available_machines = [] failed_machines = [] with database.session() as session: failed_machines = [] for host in hosts: # Check if machine exists machine = session.query(ModelMachine).filter_by(id=host)\ .first() if not machine: error_msg = "Machine id=%s does not exist!" % host return errors.handle_not_exist( errors.ObjectDoesNotExist(error_msg)) clusterhost = session.query(ModelClusterHost)\ .filter_by(machine_id=host)\ .first() if clusterhost: # Machine is already used failed_machines.append(clusterhost.machine_id) continue # Add the available machine to available_machines list available_machines.append(machine) if failed_machines: value = {'failedMachines': failed_machines} error_msg = "Conflict!" return errors.handle_duplicate_object( errors.ObjectDuplicateError(error_msg), value) for machine, host in zip(available_machines, hosts): host = ModelClusterHost(cluster_id=cluster_id, machine_id=machine.id) session.add(host) session.flush() cluster_res = {} cluster_res['id'] = host.id cluster_res['machine_id'] = machine.id cluseter_hosts.append(cluster_res) logging.info('cluster_hosts result is %s', cluseter_hosts) return util.make_json_response(200, { "status": "OK", "cluster_hosts": cluseter_hosts }) def _remove_hosts(cluster_id, hosts): """Remove existing cluster host from the cluster""" removed_hosts = [] with database.session() as session: failed_hosts = [] for host_id in hosts: host = session.query(ModelClusterHost).filter_by(id=host_id)\ .first() if not host: failed_hosts.append(host) continue host_res = {"id": host_id, "machine_id": host.machine_id} removed_hosts.append(host_res) if failed_hosts: error_msg = 'Cluster hosts do not exist!' value = {"failedHosts": failed_hosts} return errors.handle_not_exist( errors.ObjectDoesNotExist(error_msg), value) filter_clause = [] for host_id in hosts: filter_clause.append('id=%s' % host_id) # Delete the requested hosts from database session.query(ModelClusterHost).filter(or_(*filter_clause))\ .delete(synchronize_session='fetch') return util.make_json_response(200, { "status": "OK", "cluster_hosts": removed_hosts }) def _replace_all_hosts(cluster_id, hosts): """Remove all existing hosts from the cluster and add new ones""" with database.session() as session: # Delete all existing hosts of the cluster session.query(ModelClusterHost)\ .filter_by(cluster_id=cluster_id).delete() session.flush() return _add_hosts(cluster_id, hosts) def _deploy(cluster_id): """Deploy the cluster""" deploy_hosts_urls = [] with database.session() as session: cluster_hosts_ids = session.query(ModelClusterHost.id)\ .filter_by(cluster_id=cluster_id).all() if not cluster_hosts_ids: # No host belongs to this cluster error_msg = ('Cannot find any host in cluster id=%s' % cluster_id) return errors.handle_not_exist( errors.ObjectDoesNotExist(error_msg)) for elm in cluster_hosts_ids: host_id = str(elm[0]) progress_url = '/cluster_hosts/%s/progress' % host_id deploy_hosts_urls.append(progress_url) # Lock cluster hosts and its cluster session.query(ModelClusterHost).filter_by(cluster_id=cluster_id)\ .update({'mutable': False}) session.query(ModelCluster).filter_by(id=cluster_id)\ .update({'mutable': False}) celery.send_task("compass.tasks.trigger_install", (cluster_id, )) return util.make_json_response(202, { "status": "OK", "deployment": deploy_hosts_urls }) request_data = None with database.session() as session: cluster = session.query(ModelCluster).filter_by(id=cluster_id).first() if not cluster: error_msg = 'Cluster id=%s does not exist!' return errors.handle_not_exist( errors.ObjectDoesNotExist(error_msg)) if not cluster.mutable: # The cluster cannot be deploy again error_msg = "The cluster id=%s cannot deploy again!" % cluster_id return errors.handle_invalid_usage( errors.UserInvalidUsage(error_msg)) request_data = json.loads(request.data) action = request_data.keys()[0] value = request_data.get(action) if 'addHosts' in request_data: return _add_hosts(cluster_id, value) elif 'removeHosts' in request_data: return _remove_hosts(cluster_id, value) elif 'deploy' in request_data: return _deploy(cluster_id) elif 'replaceAllHosts' in request_data: return _replace_all_hosts(cluster_id, value) else: return errors.handle_invalid_usage( errors.UserInvalidUsage('%s action is not support!' % action))
def get(self): """ Lists details of machines, optionally filtered by some conditions as the following. :param switchId: the unique identifier of the switch :param vladId: the vlan ID :param port: the port number :param limit: the number of records expected to return """ machines_result = [] switch_id = request.args.get(self.SWITCHID, type=int) vlan = request.args.get(self.VLANID, type=int) port = request.args.get(self.PORT, type=int) limit = request.args.get(self.LIMIT, 0, type=int) with database.session() as session: machines = [] filter_clause = [] if switch_id: filter_clause.append('switch_id=%d' % switch_id) if vlan: filter_clause.append('vlan=%d' % vlan) if port: filter_clause.append('port=%d' % port) if limit < 0: error_msg = 'Limit cannot be less than 0!' return errors.UserInvalidUsage( errors.UserInvalidUsage(error_msg)) if filter_clause: if limit: machines = session.query(ModelMachine)\ .filter(and_(*filter_clause))\ .limit(limit).all() else: machines = session.query(ModelMachine)\ .filter(and_(*filter_clause)).all() else: if limit: machines = session.query(ModelMachine).limit(limit).all() else: machines = session.query(ModelMachine).all() logging.info('all machines: %s', machines) for machine in machines: machine_res = {} machine_res['switch_ip'] = None if not machine.switch else \ machine.switch.ip machine_res['id'] = machine.id machine_res['mac'] = machine.mac machine_res['port'] = machine.port machine_res['vlan'] = machine.vlan machine_res['link'] = { 'rel': 'self', 'href': '/'.join((self.ENDPOINT, str(machine.id))) } machines_result.append(machine_res) logging.info('machines for %s: %s', switch_id, machines_result) return util.make_json_response(200, { "status": "OK", "machines": machines_result })
def get(self): """ List details of all switches, optionally filtered by some conditions. Note: switchIp and swtichIpNetwork cannot be combined to use. :param switchIp: switch IP address :param switchIpNetwork: switch IP network :param limit: the number of records excepted to return """ qkeys = request.args.keys() logging.info('SwitchList query strings : %s', qkeys) switch_list = [] with database.session() as session: switches = [] switch_ips = request.args.getlist(self.SWITCHIP) switch_ip_network = request.args.get(self.SWITCHIPNETWORK, type=str) limit = request.args.get(self.LIMIT, 0, type=int) if switch_ips and switch_ip_network: error_msg = 'switchIp and switchIpNetwork cannot be combined!' return errors.handle_invalid_usage( errors.UserInvalidUsage(error_msg)) if limit < 0: error_msg = "limit cannot be less than 1!" return errors.handle_invalid_usage( errors.UserInvalidUsage(error_msg)) if switch_ips: for ip_addr in switch_ips: ip_addr = str(ip_addr) if not util.is_valid_ip(ip_addr): error_msg = 'SwitchIp format is incorrect!' return errors.handle_invalid_usage( errors.UserInvalidUsage(error_msg)) switch = session.query(ModelSwitch).filter_by(ip=ip_addr)\ .first() if switch: switches.append(switch) logging.info('[SwitchList][get] ip %s', ip_addr) elif switch_ip_network: # query all switches which belong to the same network if not util.is_valid_ipnetowrk(switch_ip_network): error_msg = 'SwitchIpNetwork format is incorrect!' return errors.handle_invalid_usage( errors.UserInvalidUsage(error_msg)) def get_queried_ip_prefix(network, prefix): """ Get Ip prefex as pattern used to query switches. Switches' Ip addresses need to match this pattern. """ count = int(prefix / 8) if count == 0: count = 1 return network.rsplit('.', count)[0] + '.' from netaddr import IPNetwork, IPAddress ip_network = IPNetwork(switch_ip_network) ip_filter = get_queried_ip_prefix(str(ip_network.network), ip_network.prefixlen) logging.info('ip_filter is %s', ip_filter) result_set = session.query(ModelSwitch).filter( ModelSwitch.ip.startswith(ip_filter)).all() for switch in result_set: ip_addr = str(switch.ip) if IPAddress(ip_addr) in ip_network: switches.append(switch) logging.info('[SwitchList][get] ip %s', ip_addr) if limit and len(switches) > limit: switches = switches[:limit] elif limit and not switches: switches = session.query(ModelSwitch).limit(limit).all() else: switches = session.query(ModelSwitch).all() for switch in switches: switch_res = {} switch_res['id'] = switch.id switch_res['ip'] = switch.ip switch_res['state'] = switch.state switch_res['link'] = { 'rel': 'self', 'href': '/'.join((self.ENDPOINT, str(switch.id))) } switch_list.append(switch_res) logging.info('get switch list: %s', switch_list) return util.make_json_response(200, { "status": 'OK', "switches": switch_list })