def list_components(*, node, cluster): """List the components installed on a node of a specified cluster. :param node: Machine name :type node: str :param cluster: Cluster name :type cluster: str :raises ex.LoadError: [description] :return: The list of the components installed on the node :rtype: list """ if not nodes.check_node(cluster=cluster, node=node): raise ex.LoadError('node', node, 'NotExist') if cluster != ss.svars['cluster']: try: with open(JUMBODIR + cluster + '/jumbo_config', 'r') as clf: cluster_conf = json.load(clf) except IOError as e: raise ex.LoadError('cluster', cluster, e.strerror) else: cluster_conf = ss.svars for m in cluster_conf['nodes']: if m['name'] == node: m_conf = m break return m_conf['components']
def remove_component(component, *, node, cluster): """Remove a service of a specified node in a specified cluster. :param name: Service name :type name: str :param node: Machine name :type node: str :param cluster: Cluster name :type cluster: str :raises ex.LoadError: [description] :raises ex.CreationError: [description] """ ss.load_config(cluster) if not nodes.check_node(cluster=cluster, node=node): raise ex.LoadError('node', node, 'NotExist') service = check_component(component) if not service: raise ex.LoadError('component', component, 'NotExist') for i, m in enumerate(ss.svars['nodes']): if m['name'] == node: m_index = i if component not in ss.svars['nodes'][m_index]['components']: raise ex.CreationError('node', node, 'component', component, 'NotInstalled') ss.svars['nodes'][m_index]['components'].remove(component) ss.dump_config(get_services_components_hosts())
def add_component(name, node, cluster, ha=None): """Add a component to a specified node of a specified. :param name: Component name :type name: str :param node: Machine name :type node: str :param cluster: Cluster name :type cluster: str :raises ex.LoadError: [description] :raises ex.CreationError: [description] """ for i, m in enumerate(ss.svars['nodes']): if m['name'] == node: m_index = i break else: raise ex.LoadError('node', node, 'NotExist') service = check_component(name) if not service: raise ex.LoadError('component', name, 'NotExist') if not check_service_cluster(service): raise ex.CreationError( 'cluster', cluster, 'service', service, 'NotInstalled') for i, m in enumerate(ss.svars['nodes']): if m['name'] == node: m_index = i if ha is None: ha = check_comp_number(service, name) missing_serv, missing_comp = check_service_req_service(service, ha) if missing_serv: raise ex.CreationError('component', name, 'services', missing_serv, 'ReqNotMet') if missing_comp: print_missing = [] print_missing.append('Default:') for k, v in missing_comp['default'].items(): print_missing.append(' - {} {}'.format(v, k)) print_missing.append('or High Availability:') for k, v in missing_comp['ha'].items(): print_missing.append(' - {} {}'.format(v, k)) raise ex.CreationError('service', name, 'components', print_missing, 'ReqNotMet') if name in ss.svars['nodes'][m_index]['components']: raise ex.CreationError('node', node, 'component', name, 'Installed') ss.svars['nodes'][m_index]['components'].append(name) ss.dump_config(get_services_components_hosts())
def check_and_call(*args, **kwargs): if not kwargs['cluster']: raise ex.LoadError('cluster', None, 'NoContext') elif not check_cluster(kwargs['cluster']): raise ex.LoadError('cluster', kwargs['cluster'], 'NotExist') elif kwargs['cluster'] != ss.svars['cluster']: if ss.svars['cluster']: raise ex.LoadError('cluster', ss.svars['cluster'], 'MustExit') return func(*args, **kwargs)
def check_comp_number(service, component): """Check the maximum number of a component is not already reached. :param service: Service name :type service: str :param component: Component name :type component: str :return: True if the service is in HA mode, false otherwise """ ha = 'ha' if check_ha(service) else 'default' serv_comp_host = get_services_components_hosts() number_comp = 1 if serv_comp_host[service].get(component): number_comp = len(serv_comp_host[service][component]) + 1 for s in config['services']: if s['name'] == service: for c in s['components']: if c['name'] == component: if number_comp > c['number'][ha] \ and c['number'][ha] != -1: raise ex.CreationError('cluster', ss.svars['cluster'], 'components', component, 'MaxNumber') elif number_comp == c['number']['ha']: to_remove = {} for comp in s['components']: n = 0 max_n = comp['number']['ha'] if serv_comp_host[service].get(comp['name']): n = len(serv_comp_host[service][comp['name']]) if n > max_n and max_n != -1: to_remove[comp['name']] = n - max_n if to_remove: print_remove = [] for k, v in to_remove.items(): print_remove.append('{} {}'.format(v, k)) raise ex.CreationError('service', service, 'components', print_remove, 'TooManyHA') return True return False raise ex.LoadError('component', component, 'NotExist') raise ex.LoadError('service', service, 'NotExist')
def edit_node(name, ip=None, ram=None, cpus=None, *, cluster): """Modify an existing node in a cluster. """ ss.load_config(cluster=cluster) if not check_node(cluster=cluster, node=name): raise ex.LoadError('node', name, 'NotExist') if check_ip(ip, cluster=cluster): raise ex.CreationError('node', name, 'IP', ip, 'Exists') changed = [] for i, m in enumerate(ss.svars['nodes']): if m['name'] == name: if ip: changed.append(["IP", ss.svars['nodes'][i]['ip'], ip]) ss.svars['nodes'][i]['ip'] = ip if ram: changed.append(["RAM", ss.svars['nodes'][i]['ram'], ram]) ss.svars['nodes'][i]['ram'] = ram if cpus: changed.append(["CPUs", ss.svars['nodes'][i]['cpus'], cpus]) ss.svars['nodes'][i]['cpus'] = cpus ss.dump_config() return changed
def get_yaml_config(cluster=None): """Get the versions to use for each service/platform/ressource :raises ex.LoadError: If the file versions.json doesn't exist :return: The versions to use :rtype: dict """ yaml_versions = {'services': {}, 'platform': {}} if not os.path.isfile(JUMBODIR + 'versions.json'): raise ex.LoadError('file', JUMBODIR + 'versions.json', 'NotExist') # Global versions settings with open(JUMBODIR + 'versions.json', 'r') as vs: jumbo_versions = json.load(vs) yaml_versions = update_yaml_versions(yaml_versions, jumbo_versions) if not cluster: return yaml_versions # Cluster versions settings if os.path.isfile(JUMBODIR + cluster + '/versions.json'): with open(JUMBODIR + cluster + '/versions.json', 'r') as vs: cluster_versions = json.load(vs) yaml_versions = update_yaml_versions(yaml_versions, cluster_versions) return yaml_versions
def load_config(cluster): """Load a cluster in the session. :param cluster: Cluster name :type cluster: str :return: True on success """ global svars if not checks.check_cluster(cluster): raise ex.LoadError('cluster', cluster, 'NotExist') if not clusters.check_config(cluster): raise ex.LoadError('cluster', cluster, 'NoConfFile') else: try: with open(JUMBODIR + cluster + '/jumbo_config', 'r') as jc: svars = json.load(jc) except IOError as e: raise ex.LoadError('cluster', cluster, e.strerror) vs.update_versions_file() return True
def get_abbr(component, service): """Return the abbreviation of a component. :param component: Component name :type component: str :param service: Service name of the component :type service: str """ if not check_component(component): raise ex.LoadError('component', component, 'NotExist') for s in config['services']: if s['name'] == service: for c in s['components']: if c['name'] == component: return c['abbr']
def create_cluster(domain, template=None, *, cluster): """Create a new cluster and load it in the session. :param name: New cluster name :type name: str :param domain: New cluster domain name :type domain: str :raises ex.CreationError: If name already used :return: True on creation success """ if checks.check_cluster(cluster): raise ex.CreationError('cluster', cluster, 'name', cluster, 'Exists') allowed_chars = string.ascii_letters + string.digits + '-' for l in cluster: if l not in allowed_chars: raise ex.CreationError('cluster', cluster, 'name', 'Allowed characters: ' + allowed_chars, 'NameNotAllowed') ss.clear() data_dir = os.path.dirname(os.path.abspath(__file__)) + '/data/' config_dir = os.path.dirname(os.path.abspath(__file__)) + '/config/' if template: try: with open(config_dir + 'templates/' + template + '.json') \ as template_file: ss.svars = json.load(template_file) except: raise ex.LoadError('template', template, 'NotExist') pathlib.Path(JUMBODIR + cluster).mkdir(parents=True) dir_util.copy_tree(data_dir, JUMBODIR + cluster) dir_util._path_created = {} ss.svars['cluster'] = cluster ss.svars['domain'] = domain if domain else '%s.local' % cluster services_components_hosts = None if template: services_components_hosts = services.get_services_components_hosts() ss.dump_config(services_components_hosts) return True
def list_clusters(): """List all the clusters managed by Jumbo. :raises ex.LoadError: If a cluster doesn't have a 'jumbo_config' file :return: The list of clusters' configurations :rtype: dict """ path_list = [f.path for f in os.scandir(JUMBODIR) if f.is_dir()] clusters = [] for p in path_list: if not check_config(p.split('/')[-1]): raise ex.LoadError('cluster', p.split('/')[-1], 'NoConfFile') with open(p + '/jumbo_config') as cfg: clusters += [json.load(cfg)] return clusters
def add_service(name, ha=False, *, cluster): """Add a service to a specified cluster. :param name: Service name :type name: str :param cluster: Cluster name :type cluster: str :raises ex.LoadError: [description] :raises ex.CreationError: [description] """ ss.load_config(cluster) if not check_service(name): raise ex.LoadError('service', name, 'NotExist') if name in ss.svars['services']: raise ex.CreationError('cluster', cluster, 'service', name, 'Installed') if check_service_cluster(name): raise ex.CreationError( 'cluster', cluster, 'service', name, 'Installed') missing_serv, missing_comp = check_service_req_service(name, ha) if missing_serv: raise ex.CreationError('service', name, 'services', (' - %s' % s for s in missing_serv), 'ReqNotMet') if missing_comp: print_missing = [] print_missing.append('Default:') for k, v in missing_comp['default'].items(): print_missing.append(' - {} {}'.format(v, k)) print_missing.append('or High Availability:') for k, v in missing_comp['ha'].items(): print_missing.append(' - {} {}'.format(v, k)) raise ex.CreationError('service', name, 'components', print_missing, 'ReqNotMet') ss.svars['services'].append(name) auto_install_service(name, cluster, ha) ss.dump_config(get_services_components_hosts())
def check_ha(service): """Check if a service is in HA mode. :param service: Service name :type service: str :return: True if the service is in HA mode, False otherwise """ serv_comp_host = get_services_components_hosts() for s in config['services']: if s['name'] == service: for c in s['components']: if serv_comp_host[service].get(c['name']): number_comp = len(serv_comp_host[service][c['name']]) if number_comp > c['number']['default'] \ and c['number']['ha'] > c['number']['default']: return True return False raise ex.LoadError('service', service, 'NotExist')
def auto_assign(service, ha, *, cluster): """Auto-install a service and its components on the best fitting hosts. :param service: Service name :type service: str :param cluster: Cluster name :type cluster: str :raises ex.LoadError: If the cluster doesn't exist :raises ex.CreationError: If the requirements are not met to install """ ss.load_config(cluster) scfg = check_service(service) if not scfg: raise ex.LoadError('service', service, 'NotExist') # dist is 'default' or 'ha' dist = 'ha' if ha else 'default' # Check loop for atomicity for component in scfg['components']: left = auto_assign_service_comp(component, dist, cluster, check=True) if left == -1: raise ex.CreationError('component', component['name'], 'hosts type (need at least 1 of them)', (' - %s' % c for c in component['hosts_types']), 'ReqNotMet') elif left > 0: raise ex.CreationError('component', component['name'], 'hosts type (need ' + str(left) + ' of them)', (' - %s' % c for c in component['hosts_types']), 'ReqNotMet') count = 0 for component in scfg['components']: auto_assign_service_comp(component, dist, cluster, check=False) count += 1 return count
def delete_cluster(*, cluster): """Delete a cluster. :param name: Cluster name :type name: str :raises ex.LoadError: If the cluster doesn't exist :return: True if the deletion was successfull """ try: # Vagrant destroy current_dir = os.getcwd() os.chdir(JUMBODIR + cluster + '/') subprocess.check_output(['vagrant', 'destroy', '-f']) os.chdir(current_dir) rmtree(JUMBODIR + cluster) except IOError as e: raise ex.LoadError('cluster', cluster, e.strerror) ss.clear() return True
def remove_node(*, cluster, node): """Remove a node in a cluster. :param cluster: Cluster namee :type cluster: str :param name: Machine name :type name: str :raises ex.LoadError: If the node or the cluster couldn't be loaded :return: True if the session context has changed :rtype: bool """ ss.load_config(cluster) if not check_node(cluster=cluster, node=node): raise ex.LoadError('node', node, 'NotExist') for i, m in enumerate(ss.svars['nodes']): if m['name'] == node: del (ss.svars['nodes'][i]) ss.dump_config()
def check_service_req_service(name, ha=False): """Check if a service requirements are satisfied. :param name: Service name :type name: str :param ha: Weither the service is in HA, defaults to False :raises ex.LoadError: If the service doesn't exist :return: The misssing services and components needed to install the service :rtype: dict """ req = 'ha' if ha else 'default' missing_serv = [] missing_comp = {} for s in config['services']: if s['name'] == name: for req_s in s['requirements']['services'][req]: if req_s not in ss.svars['services']: missing_serv.append(req_s) missing_comp.update(check_service_req_comp(req_s)) return missing_serv, missing_comp raise ex.LoadError('service', name, 'NotExist')
def remove_service(service, *, cluster): """Remove a service of a specified cluster. :param name: Service name :type name: str :param cluster: Cluster name :type cluster: str :raises ex.LoadError: [description] :raises ex.CreationError: [description] """ ss.load_config(cluster) if not check_service(service): raise ex.LoadError('service', service, 'NotExist') if not check_service_cluster(service): raise ex.CreationError( 'cluster', cluster, 'service', service, 'NotInstalled') dependent = check_dependent_services(service) if dependent: raise ex.CreationError( 'service', service, 'services', dependent, 'Dependency' ) serv_comp = get_service_components(service) for m in ss.svars['nodes']: to_remove = [] for c in m['components']: if c in serv_comp: to_remove.append(c) for c in to_remove: m['components'].remove(c) ss.svars['services'].remove(service) ss.dump_config(get_services_components_hosts())
def check_service_req_comp(name): """Check if all the components required are installed for a service. :param name: Service name :type name: str :param ha: Weither the service is in HA, defaults to False :raises ex.LoadError: If the service doesn't exist :return: The missing components needed to install the service :rtype: dict """ missing = { 'default': {}, 'ha': {} } comp_count = count_components() for s in config['services']: if s['name'] == name: for comp in s['components']: req = 'default' req_number = comp['number'][req] if comp['number'][req] != -1 \ else 1 missing_count = req_number - comp_count[comp['name']] if missing_count > 0: missing['default'][comp['name']] = missing_count req = 'ha' req_number = comp['number'][req] if comp['number'][req] != -1 \ else 1 missing_count = req_number - comp_count[comp['name']] if missing_count > 0: missing['ha'][comp['name']] = missing_count if not missing['ha'] or not missing['default']: return {} return missing raise ex.LoadError('service', name, 'NotExist')
def get_component(name): for s in config['services']: for c in s['components']: if c['name'] == name: return c raise ex.LoadError('component', name, 'NotExist')