diff --git a/bin/client.py b/bin/client.py index a13a1f13..1cfc451d 100755 --- a/bin/client.py +++ b/bin/client.py @@ -16,10 +16,12 @@ """binary to deploy a cluster by compass client api.""" import logging +import netaddr import os import re import requests import site +import socket import sys import time @@ -48,7 +50,7 @@ flags.add('switch_ips', default='') flags.add('switch_credential', help='comma separated =', - default='version=v2c,community=public') + default='version=2c,community=public') flags.add('switch_max_retries', type='int', help='max retries of poll switch', default=5) @@ -61,39 +63,90 @@ flags.add_bool('poll_switches', flags.add('machines', help='comma separated mac addresses of machines', default='') -flags.add('adapter_os_name', +flags.add('subnets', + help='comma seperated subnets', + default='') +flags.add('adapter_os_pattern', help='adapter os name', default=r'(?i)centos.*') -flags.add('adapter_target_system', +flags.add('adapter_target_system_pattern', help='adapter target system name', - default='openstack') -flags.add('adapter_flavor', + default='openstack.*') +flags.add('adapter_flavor_pattern', help='adapter flavor name', default='allinone') flags.add('cluster_name', help='cluster name', default='cluster1') -flags.add('credentials', +flags.add('language', + help='language', + default='EN') +flags.add('timezone', + help='timezone', + default='GMT') +flags.add('http_proxy', + help='http proxy', + default='') +flags.add('https_proxy', + help='https proxy', + default='') +flags.add('no_proxy', + help='no proxy', + default='') +flags.add('ntp_server', + help='ntp server', + default='') +flags.add('dns_servers', + help='dns servers', + default='') +flags.add('domain', + help='domain', + default='') +flags.add('search_path', + help='search path', + default='') +flags.add('default_gateway', + help='default gateway', + default='') +flags.add('server_credential', help=( - 'comma separated credentials formatted as ' - ':=' + 'server credential formatted as ' + '=' ), - default=( - 'server:root=root,service:service=service,' - 'console:console=console' - )) -flags.add('networking', + default='root=root') +flags.add('service_credentials', help=( - 'semicomma seperated network property and its value ' - '=' + 'comma seperated service credentials formatted as ' + ':=,...' + ), + default='') +flags.add('console_credentials', + help=( + 'comma seperated console credential formated as ' + ':=' + ), + default='') +flags.add('hostnames', + help='comma seperated hostname', + default='') +flags.add('host_networks', + help=( + 'semicomma seperated host name and its networks ' + ':=||,...' ), default='') flags.add('partitions', help=( 'comma seperated partitions ' - ':=' + '=' ), - default='tmp:percentage=10,var:percentage=20,home:percentage=40') + default='tmp:percentage=10%,var:percentage=30%,home:percentage=30%') +flags.add('network_mapping', + help=( + 'comma seperated network mapping ' + '=' + ), + default='') flags.add('host_roles', help=( 'semicomma separated host roles ' @@ -106,9 +159,9 @@ flags.add('deployment_timeout', flags.add('progress_update_check_interval', help='progress update status check interval in seconds', default=60) -flags.add('dashboard_role', - help='dashboard role name', - default='os-dashboard') +flags.add('dashboard_url', + help='dashboard url', + default='') flags.add('dashboard_link_pattern', help='dashboard link pattern', default=r'(?m)(http://\d+\.\d+\.\d+\.\d+:5000/v2\.0)') @@ -186,6 +239,10 @@ def _poll_switches(client): switch_ip for switch_ip in flags.OPTIONS.switch_ips.split(',') if switch_ip ] + if not switch_ips: + raise Exception( + 'there is no switches to poll') + switch_credential = dict([ credential.split('=', 1) for credential in flags.OPTIONS.switch_credential.split(',') @@ -206,6 +263,15 @@ def _poll_switches(client): remain_retries = flags.OPTIONS.switch_max_retries while True: + for switch_ip, switch in all_switches.items(): + status, resp = client.poll_switch(switch['id']) + logging.info( + 'get switch %s status %s: %s', + switch_ip, status, resp) + if status >= 400: + msg = 'failed to update switch %s' % switch_ip + raise Exception(msg) + remain_retries -= 1 time.sleep(flags.OPTIONS.switch_retry_interval) for switch_ip, switch in all_switches.items(): switch_id = switch['id'] @@ -237,15 +303,7 @@ def _poll_switches(client): except Exception: logging.error('failed to get all machines') - if remain_retries > 0: - for switch_ip, switch in all_switches.items(): - status, resp = client.poll_switch(switch['id']) - if status >= 400: - msg = 'failed to update switch %s' % switch_ip - raise Exception(msg) - - remain_retries -= 1 - else: + if remain_retries <= 0: msg = 'max retries reached' raise Exception(msg) @@ -261,17 +319,19 @@ def _get_adapter(client): msg = 'failed to get adapters' raise Exception(msg) - os_name_pattern = flags.OPTIONS.adapter_os_name - os_name_re = re.compile(os_name_pattern) - target_system_pattern = flags.OPTIONS.adapter_target_system + os_pattern = flags.OPTIONS.adapter_os_pattern + os_re = re.compile(os_pattern) + target_system_pattern = flags.OPTIONS.adapter_target_system_pattern target_system_re = re.compile(target_system_pattern) - flavor_name_pattern = flags.OPTIONS.adapter_flavor - flavor_re = re.compile(flavor_name_pattern) + flavor_pattern = flags.OPTIONS.adapter_flavor_pattern + flavor_re = re.compile(flavor_pattern) adapter_id = None os_id = None flavor_id = None adapter = None for item in resp: + if 'distributed_system_name' not in item: + continue if target_system_re.match(item['distributed_system_name']): adapter = item adapter_id = adapter['id'] @@ -282,12 +342,12 @@ def _get_adapter(client): raise Exception(msg) for supported_os in adapter['supported_oses']: - if os_name_re.match(supported_os['name']): + if os_re.match(supported_os['name']): os_id = supported_os['os_id'] break if not os_id: - msg = 'no os found for %s' % os_name_pattern + msg = 'no os found for %s' % os_pattern raise Exception(msg) for flavor in adapter['flavors']: @@ -296,20 +356,39 @@ def _get_adapter(client): break if not flavor_id: - msg = 'no flavor found for %s' % flavor_name_pattern + msg = 'no flavor found for %s' % flavor_pattern raise Exception(msg) logging.info('adpater for deploying a cluster: %s', adapter_id) return (adapter_id, os_id, flavor_id) -def _add_subnet(client): - pass +def _add_subnets(client): + subnets = [ + subnet for subnet in flags.OPTIONS.subnets.split(',') + if subnet + ] + if not subnets: + raise Exception( + 'there is no subnets added') + subnet_mapping = {} + for subnet in subnets: + status, resp = client.add_subnet(subnet) + logging.info('add subnet %s status %s response %s', + subnet, status, resp) + if status >= 400: + msg = 'failed to add subnet %s' % subnet + raise Exception(msg) + subnet_mapping[resp['subnet']] = resp['id'] + return subnet_mapping def _add_cluster(client, adapter_id, os_id, flavor_id, machines): """add a cluster.""" cluster_name = flags.OPTIONS.cluster_name + if not cluster_name: + raise Exception( + 'no cluster name set') status, resp = client.add_cluster( cluster_name, adapter_id, os_id, flavor_id) @@ -322,321 +401,417 @@ def _add_cluster(client, adapter_id, os_id, flavor_id, machines): cluster = resp cluster_id = cluster['id'] + hostnames = [ + hostname for hostname in flags.OPTIONS.hostnames.split(',') + if hostname + ] + if len(machines) != len(hostnames): + msg = 'hostname %s length does not match machines mac %s length' % ( + hostnames, machines) + raise Exception(msg) + machines_dict = [] - for machine_id in machines: + for machine_id, hostname in map(None, machines, hostnames): machines_dict.append({ - 'machine_id': machine_id + 'machine_id': machine_id, + 'name': hostname }) # add hosts to the cluster. status, resp = client.add_hosts_to_cluster( cluster_id, - machines_dict) - logging.info('add hosts to cluster %s status: %s, resp: %s', - cluster_id, status, resp) + {'machines': machines_dict}) + logging.info('add machines %s to cluster %s status: %s, resp: %s', + machines_dict, cluster_id, status, resp) if status >= 400: msg = 'failed to add machines %s to cluster %s' % ( machines, cluster_name) raise Exception(msg) - - host_ids = [] - for host in resp['cluster_hosts']: - host_ids.append(host['id']) - - logging.info('added hosts in cluster %s: %s', cluster_id, host_ids) - if len(host_ids) != len(machines): + host_mapping = {} + for host in resp['hosts']: + host_mapping[host['hostname']] = host['id'] + logging.info('added hosts in cluster %s: %s', cluster_id, host_mapping) + if len(host_mapping) != len(machines): msg = 'machines %s to add to the cluster %s while hosts %s' % ( - machines, cluster_name, host_ids) + machines, cluster_name, host_mapping) raise Exception(msg) - - return {cluster_id: host_ids} + return (cluster_id, host_mapping) -def _set_cluster_security(client, cluster_hosts): - """set cluster security.""" - credentials = [ - credential for credential in flags.OPTIONS.credentials.split(',') - if ':' in credential +def _set_cluster_os_config(client, cluster_id, host_ips): + """set cluster os config.""" + os_config = {} + language = flags.OPTIONS.language + timezone = flags.OPTIONS.timezone + http_proxy = flags.OPTIONS.http_proxy + https_proxy = flags.OPTIONS.https_proxy + if not https_proxy and http_proxy: + https_proxy = http_proxy + no_proxy = [ + no_proxy for no_proxy in flags.OPTIONS.no_proxy.split(',') + if no_proxy ] - logging.info('set cluster security: %s', credentials) - credential_mapping = {} - for credential in credentials: - credential_name, username_and_password = credential.split(':', 1) - if not credential_name: - raise Exception('there is no credential name in %s' % credential) - - if not username_and_password: - raise Exception('there is no username/password in %s' % credential) - - if '=' not in username_and_password: - raise Exception('there is no = in %s' % username_and_password) - - username, password = username_and_password.split('=', 1) - if not username or not password: - raise Exception( - 'there is no username or password in %s' % ( - username_and_password)) - - credential_mapping['%s_username' % credential_name] = username - credential_mapping['%s_password' % credential_name] = password - - for cluster_id, host_ids in cluster_hosts.items(): - status, resp = client.set_security( - cluster_id, **credential_mapping) - logging.info( - 'set security config to cluster %s status: %s, resp: %s', - cluster_id, status, resp) - if status >= 400: - msg = 'failed to set security %s for cluster %s' % ( - credential_mapping, cluster_id) - raise Exception(msg) - - -def _set_cluster_networking(client, cluster_hosts): - """set cluster networking.""" - networking_map = {} - networkings = [ - network for network in flags.OPTIONS.networking.split(';') - if '=' in network + compass_name = socket.gethostname() + compass_ip = socket.gethostbyname(compass_name) + if http_proxy and not no_proxy: + no_proxy = ['127.0.0.1', compass_name, compass_ip] + for hostname, ips in host_ips.items(): + no_proxy.append(hostname) + no_proxy.extend(ips) + ntp_server = flags.OPTIONS.ntp_server + if not ntp_server: + ntp_server = compass_ip + dns_servers = [ + dns_server for dns_server in flags.OPTIONS.dns_servers.split(',') + if dns_server ] - logging.info('set cluster networking: %s', networkings) - for networking in networkings: - networking_name, networking_value = networking.split('=', 1) - if not networking_name: - raise Exception( - 'there is no networking name in %s' % networking) - - if networking_name.endswith('_promisc'): - networking_map[networking_name] = int(networking_value) - else: - networking_map[networking_name] = networking_value - - for cluster_id, host_ids in cluster_hosts.items(): - status, resp = client.set_networking( - cluster_id, **networking_map) - logging.info( - 'set networking config %s to cluster %s status: %s, resp: %s', - networking_map, cluster_id, status, resp) - if status >= 400: - msg = 'failed to set networking config %s to cluster %s' % ( - networking_map, cluster_id) - raise Exception(msg) - - -def _set_cluster_partition(client, cluster_hosts): - """set partiton of each host in cluster.""" + if not dns_servers: + dns_servers = [compass_ip] + domain = flags.OPTIONS.domain + if not domain: + raise Exception('domain is not defined') + search_path = [ + search_path for search_path in flags.OPTIONS.search_path.split(',') + if search_path + ] + if not search_path: + search_path = [domain] + default_gateway = flags.OPTIONS.default_gateway + if not default_gateway: + raise Exception('default gateway is not defined') + os_config['general'] = { + 'language': language, + 'timezone': timezone, + 'ntp_server': ntp_server, + 'dns_servers': dns_servers, + 'default_gateway': default_gateway + } + if http_proxy: + os_config['general']['http_proxy'] = http_proxy + if https_proxy: + os_config['general']['https_proxy'] = https_proxy + if no_proxy: + os_config['general']['no_proxy'] = no_proxy + if domain: + os_config['general']['domain'] = domain + if search_path: + os_config['general']['search_path'] = search_path + server_credential = flags.OPTIONS.server_credential + if '=' in server_credential: + server_username, server_password = server_credential.split('=', 1) + elif server_credential: + server_username = server_credential + server_password = server_username + else: + server_username = 'root' + server_password = 'root' + os_config['server_credentials'] = { + 'username': server_username, + 'password': server_password + } partitions = [ partition for partition in flags.OPTIONS.partitions.split(',') - if ':' in partition ] - logging.info('set cluster partition: %s', partitions) - partiton_mapping = {} + os_config['partition'] = {} for partition in partitions: - partition_name, partition_pair = partition.split(':', 1) + partition_name, partition_value = partition.split('=', 1) if not partition_name: raise Exception( 'there is no partition name in %s' % partition) - - if not partition_pair: + if not partition_value: raise Exception( - 'there is no partition pair in %s' % partition) + 'there is no partition value in %s' % partition) - if '=' not in partition_pair: - raise Exception( - 'there is no = in %s' % partition_pair) - - partition_type, partition_value = partition_pair.split('=', 1) - if partition_type == 'percentage': - partition_value = int(partition_value) - elif partition_type == 'mbytes': - partition_value = int(partition_value) + if partition_value.endswith('%'): + partition_type = 'percentage' + partition_value = int(partition_value[:-1]) else: - raise Exception( - 'unsupported partition type %s' % partition_type) - - partiton_mapping[ - '%s_%s' % (partition_name, partition_type) - ] = partition_value - - for cluster_id, host_ids in cluster_hosts.items(): - status, resp = client.set_partition( - cluster_id, **partiton_mapping) - logging.info( - 'set partition config %s to cluster %s status: %s, resp: %s', - partiton_mapping, cluster_id, status, resp) - if status >= 400: - msg = 'failed to set partition %s to cluster %s' % ( - partiton_mapping, cluster_id) - raise Exception(msg) - - -def _set_host_config(client, cluster_hosts): - host_configs = [] - for host in flags.OPTIONS.host_roles.split(';'): - if not host: - continue - - hostname, roles = host.split('=', 1) - if hostname: - roles = [role for role in roles.split(',') if role] - - host_configs.append({ - 'hostname': hostname, - 'roles': roles - }) - - total_hosts = 0 - for cluster_id, host_ids in cluster_hosts.items(): - total_hosts += len(host_ids) - - if total_hosts != len(host_configs): - msg = '%s host to assign but got %s host configs' % ( - total_hosts, len(host_configs)) + partition_type = 'size' + os_config['partition'][partition_name] = { + partition_type: partition_value + } + status, resp = client.update_cluster_config( + cluster_id, os_config=os_config) + logging.info( + 'set os config %s to cluster %s status: %s, resp: %s', + os_config, cluster_id, status, resp) + if status >= 400: + msg = 'failed to set os config %s to cluster %s' % ( + os_config, cluster_id) raise Exception(msg) - for cluster_id, host_ids in cluster_hosts.items(): - for hostid in host_ids: - host_config = host_configs.pop(0) - status, resp = client.update_host_config( - hostid, **host_config) + +def _set_host_networking(client, host_mapping, subnet_mapping): + """set cluster hosts networking.""" + host_ips = {} + for host_network in flags.OPTIONS.host_networks.split(';'): + hostname, networks_str = host_network.split(':', 1) + if hostname not in host_mapping: + msg = 'hostname %s does not exist in host mapping %s' % ( + hostname, host_mapping + ) + raise Exception(msg) + host_id = host_mapping[hostname] + networks = networks_str.split(',') + for network in networks: + interface, network_properties_str = network.split('=', 1) + network_properties = network_properties_str.split('|') + ip_addr = network_properties[0] + ip = netaddr.IPAddress(ip_addr) + subnet_id = None + for subnet_addr, subnetid in subnet_mapping.items(): + subnet = netaddr.IPNetwork(subnet_addr) + if ip in subnet: + subnet_id = subnetid + break + if not subnet_id: + msg = 'no subnet found for ip %s' % ip_addr + raise Exception(msg) + properties = dict([ + (network_property, True) + for network_property in network_properties[1:] + ]) logging.info( - 'set host %s config %s status: %s, resp: %s', - hostid, host_config, status, resp + 'add host %s interface %s ip %s network proprties %s', + hostname, interface, ip_addr, properties) + status, response = client.add_host_network( + host_id, interface, ip=ip_addr, subnet_id=subnet_id, + **properties + ) + logging.info( + 'add host %s interface %s ip %s network properties %s ' + 'status %s: %s', + hostname, interface, ip_addr, properties, + status, response ) if status >= 400: - msg = 'failed to set host %s config %s' % ( - hostid, host_config) + msg = 'failed to set host %s interface %s network' % ( + hostname, interface + ) raise Exception(msg) + host_ips.setdefault(hostname, []).append(ip_addr) + return host_ips -def _deploy_clusters(client, cluster_hosts): - """deploy cluster.""" - for cluster_id, host_ids in cluster_hosts.items(): - status, resp = client.deploy_hosts(cluster_id) +def _set_cluster_package_config(client, cluster_id): + """set cluster package config.""" + package_config = { + 'security': { + 'service_credentials': { + }, + 'console_credentials': { + } + } + } + service_credentials = [ + service_credential + for service_credential in flags.OPTIONS.service_credentials.split(',') + if service_credential + ] + for service_credential in service_credentials: + if ':' not in service_credential: + raise Exception( + 'there is no : in service credential %s' % service_credential + ) + service_name, service_pair = service_credential.split(':', 1) + if '=' not in service_pair: + raise Exception( + 'there is no = in service %s security' % service_name + ) + username, password = service_pair.split('=', 1) + package_config['security']['service_credentials'][service_name] = { + 'username': username, + 'password': password + } + console_credentials = [ + console_credential + for console_credential in flags.OPTIONS.console_credentials.split(',') + if console_credential + ] + for console_credential in console_credentials: + if ':' not in console_credential: + raise Exception( + 'there is no : in console credential %s' % console_credential + ) + console_name, console_pair = console_credential.split(':', 1) + if '=' not in console_pair: + raise Exception( + 'there is no = in console %s security' % console_name + ) + username, password = console_pair.split('=', 1) + package_config['security']['console_credentials'][service_name] = { + 'username': username, + 'password': password + } + package_config['network_mapping'] = dict([ + network_pair.split('=', 1) + for network_pair in flags.OPTIONS.network_mapping.split(',') + if '=' in network_pair + ]) + status, resp = client.update_cluster_config( + cluster_id, package_config=package_config) + logging.info( + 'set package config %s to cluster %s status: %s, resp: %s', + package_config, cluster_id, status, resp) + if status >= 400: + msg = 'failed to set package config %s to cluster %s' % ( + package_config, cluster_id) + raise Exception(msg) + + +def _set_host_roles(client, cluster_id, host_mapping): + host_roles = {} + for host_str in flags.OPTIONS.host_roles.split(';'): + if not host_str: + continue + hostname, roles_str = host_str.split('=', 1) + if hostname not in host_mapping: + raise Exception( + 'hostname %s not found in host mapping %s' % ( + hostname, host_mapping + ) + ) + host_id = host_mapping[hostname] + roles = [role for role in roles_str.split(',') if role] + status, response = client.update_cluster_host( + cluster_id, host_id, roles=roles) logging.info( - 'deploy cluster %s status: %s, resp: %s', - cluster_id, status, resp) + 'set cluster %s host %s roles %s status %s: %s', + cluster_id, hostname, roles, status, response + ) if status >= 400: - msg = 'failed to deploy cluster %s' % cluster_id - raise Exception(msg) + raise Exception( + 'failed to set cluster %s host %s roles %s' % ( + cluster_id, host_id, roles + ) + ) + host_roles[hostname] = roles + return host_roles -def _get_installing_progress(client, cluster_hosts): +def _deploy_clusters(client, cluster_id, host_mapping): + """deploy cluster.""" + host_ids = [host_id for _, host_id in host_mapping.items()] + status, response = client.review_cluster( + cluster_id, review={'hosts': host_ids} + ) + logging.info( + 'review cluster %s hosts %s, status %s: %s', + cluster_id, host_ids, status, response + ) + if status >= 400: + raise Exception( + 'review cluster %s fails' % cluster_id + ) + status, response = client.deploy_cluster( + cluster_id, deploy={'hosts': host_ids} + ) + logging.info( + 'deploy cluster %s hosts %s status %s: %s', + cluster_id, host_ids, status, response + ) + if status >= 400: + raise Exception( + 'deploy cluster %s fails' % cluster_id + ) + + +def _get_installing_progress(client, cluster_id, host_mapping): """get intalling progress.""" timeout = time.time() + 60 * float(flags.OPTIONS.deployment_timeout) - clusters_progress = {} - hosts_progress = {} + cluster_installed = False + cluster_failed = False + hosts_installed = {} + hosts_failed = {} install_finished = False - failed_hosts = {} - failed_clusters = {} while time.time() < timeout: - found_installing_clusters = False - found_installing_hosts = False - for cluster_id, host_ids in cluster_hosts.items(): - for hostid in host_ids: - if hostid in hosts_progress: - continue - - status, resp = client.get_host_installing_progress(hostid) - logging.info( - 'get host %s installing progress status: %s, resp: %s', - hostid, status, resp) - if status >= 400: - msg = 'failed to get host %s progress' % hostid - raise Exception(msg) - - progress = resp['progress'] - if ( - progress['state'] not in ['UNINITIALIZED', 'INSTALLING'] or - progress['percentage'] >= 1.0 - ): - hosts_progress[hostid] = progress - if progress['state'] in ['ERROR']: - failed_hosts[hostid] = progress - - else: - found_installing_hosts = True - - if cluster_id in clusters_progress: - continue - - status, resp = client.get_cluster_installing_progress(cluster_id) + status, cluster_state = client.get_cluster_state(cluster_id) + logging.info( + 'get cluster %s state status %s: %s', + cluster_id, status, cluster_state + ) + if status >= 400: + raise Exception( + 'failed to acquire cluster %s state' % cluster_id + ) + if cluster_state['state'] == 'SUCCESSFUL': + cluster_installed = True + if cluster_state['state'] == 'ERROR': + cluster_failed = True + for hostname, host_id in host_mapping.items(): + status, host_state = client.get_cluster_host_state( + cluster_id, host_id + ) logging.info( - 'get cluster %s installing progress status: %s, resp: %s', - cluster_id, status, resp) + 'get cluster %s host %s state status %s: %s', + cluster_id, host_id, status, host_state + ) if status >= 400: - msg = 'failed to get cluster %s intsalling progress' % ( - cluster_id) - raise Exception(msg) - - progress = resp['progress'] - if ( - progress['state'] not in ['UNINITIALIZED', 'INSTALLING'] or - progress['percentage'] >= 1.0 - ): - clusters_progress[cluster_id] = progress - if progress['state'] in ['ERROR']: - failed_clusters[cluster_id] = progress - + raise Exception( + 'failed to acquire cluster %s host %s state' % ( + cluster_id, host_id + ) + ) + if host_state['state'] == 'SUCCESSFUL': + hosts_installed[host_id] = True else: - found_installing_clusters = True + hosts_installed[host_id] = False + if host_state['state'] == 'ERROR': + hosts_failed[host_id] = True + else: + hosts_failed[host_id] = False - if found_installing_clusters and found_installing_hosts: + cluster_finished = cluster_installed or cluster_failed + hosts_finished = {} + for _, host_id in host_mapping.items(): + hosts_finished[host_id] = ( + hosts_installed.get(host_id, False) or + hosts_failed.get(host_id, False) + ) + if cluster_finished and all(hosts_finished.values()): + logging.info('all clusters/hosts are installed.') + install_finished = True + break + else: logging.info( 'there are some clusters/hosts in installing.' 'sleep %s seconds and retry', flags.OPTIONS.progress_update_check_interval) time.sleep(float(flags.OPTIONS.progress_update_check_interval)) - else: - install_finished = True - logging.info('all clusters/hosts are installed.') - break if not install_finished: - msg = 'installing %s is not all finished: hosts %s clusters %s' % ( - cluster_hosts, hosts_progress, clusters_progress) - raise Exception(msg) - - if failed_hosts: - msg = 'installing hosts failed: %s' % failed_hosts - raise Exception(msg) - - if failed_clusters: - msg = 'installing clusters failed: %s' % failed_clusters + raise Exception( + 'cluster %s installation not finished: ' + 'installed %s, failed: %s' % ( + cluster_id, hosts_installed, hosts_failed + ) + ) + if cluster_failed or any(hosts_failed.values()): + msg = 'cluster %s hosts %s is not all finished. failed hosts %s' % ( + cluster_id, host_mapping.values(), hosts_failed.keys() + ) raise Exception(msg) -def _check_dashboard_links(client, cluster_hosts): - dashboard_role = flags.OPTIONS.dashboard_role +def _check_dashboard_links(client, cluster_id): + dashboard_url = flags.OPTIONS.dashboard_url + if not dashboard_url: + raise Exception( + 'no dashboard url set') dashboard_link_pattern = re.compile( flags.OPTIONS.dashboard_link_pattern) - for cluster_id, host_ids in cluster_hosts.items(): - status, resp = client.get_dashboard_links(cluster_id) + r = requests.get(dashboard_url, verify=False) + r.raise_for_status() + match = dashboard_link_pattern.search(r.text) + if match: logging.info( - 'get cluster %s dashboard links status: %s, resp: %s', - cluster_id, status, resp) - if status >= 400: - msg = 'failed to get cluster %s dashboard links' % cluster_id - raise Exception(msg) - - dashboardlinks = resp['dashboardlinks'] - if dashboard_role not in dashboardlinks: - msg = 'no dashboard role %s found in %s' % ( - dashboard_role, dashboardlinks) - raise Exception(msg) - - r = requests.get(dashboardlinks[dashboard_role], verify=False) - r.raise_for_status() - match = dashboard_link_pattern.search(r.text) - if match: - logging.info( - 'dashboard login page for cluster %s can be downloaded', - cluster_id) - else: - msg = ( - '%s dashboard login page failed to be downloaded\n' - 'the context is:\n%s\n' - ) % (dashboard_role, r.text) - raise Exception(msg) + 'dashboard login page for cluster %s can be downloaded', + cluster_id) + else: + msg = ( + '%s failed to be downloaded\n' + 'the context is:\n%s\n' + ) % (dashboard_url, r.text) + raise Exception(msg) def main(): @@ -648,16 +823,19 @@ def main(): machines = _poll_switches(client) else: machines = _get_machines(client) + subnet_mapping = _add_subnets(client) adapter_id, os_id, flavor_id = _get_adapter(client) - cluster_hosts = _add_cluster( + cluster_id, host_mapping = _add_cluster( client, adapter_id, os_id, flavor_id, machines) - _set_cluster_security(client, cluster_hosts) - _set_cluster_networking(client, cluster_hosts) - _set_cluster_partition(client, cluster_hosts) - _set_host_config(client, cluster_hosts) - _deploy_clusters(client, cluster_hosts) - _get_installing_progress(client, cluster_hosts) - _check_dashboard_links(client, cluster_hosts) + host_ips = _set_host_networking( + client, host_mapping, subnet_mapping + ) + _set_cluster_os_config(client, cluster_id, host_ips) + _set_cluster_package_config(client, cluster_id) + _set_host_roles(client, cluster_id, host_mapping) + _deploy_clusters(client, cluster_id, host_mapping) + _get_installing_progress(client, cluster_id, host_mapping) + _check_dashboard_links(client, cluster_id) if __name__ == "__main__": diff --git a/bin/client.sh b/bin/client.sh new file mode 100755 index 00000000..f39a717b --- /dev/null +++ b/bin/client.sh @@ -0,0 +1,2 @@ +#!/bin/bash +/opt/compass/bin/client.py --switch_ips=172.29.8.40 --machines=00:0c:29:a7:ea:4b --subnets=10.145.88.0/23,172.16.0.0/16 --cluster_name=cluster1 --domain=ods.com --default_gateway=10.145.88.1 --service_credentials=image:service=service,compute:service=service,dashboard:service=service,identity:service=service,metering:service=service,rabbitmq:service=service,volume:service=service,mysql:service=service --console_credentials=admin:console=console,compute:console=console,dashboard:console=console,image:console=console,metering:console=console,network:console=console,object-store:console=console,volume:console=console --hostnames=host1 --host_networks="host1:eth0=10.145.89.201|is_mgmt,eth1=172.16.100.201|is_promiscuous" --partitions="/var=50%,/home=30%" --network_mapping=management=eth0,tenant=eth0,storage=eth0,public=eth1 --host_roles=host1=allinone-compute --dashboard_url=http://10.145.89.201 diff --git a/compass/apiclient/example.py b/compass/apiclient/example.py index 00824d33..ce5d8d1c 100755 --- a/compass/apiclient/example.py +++ b/compass/apiclient/example.py @@ -16,34 +16,25 @@ """Example code to deploy a cluster by compass client api.""" import os import re -import requests import sys import time # from compass.apiclient.restful import Client from restful import Client -COMPASS_SERVER_URL = 'http://10.145.89.100/api' +COMPASS_SERVER_URL = 'http://localhost/api' COMPASS_LOGIN_EMAIL = 'admin@huawei.com' COMPASS_LOGIN_PASSWORD = 'admin' SWITCH_IP = '172.29.8.40' SWITCH_SNMP_VERSION = '2c' SWITCH_SNMP_COMMUNITY = 'public' -#MACHINES_TO_ADD = ['00:0c:29:05:bd:eb'] CLUSTER_NAME = 'test_cluster' HOST_NAME_PREFIX = 'host' -SERVER_USERNAME = 'root' -SERVER_PASSWORD = 'root' SERVICE_USERNAME = 'service' SERVICE_PASSWORD = 'service' -DASHBOARD_USERNAME = 'console' -DASHBOARD_PASSWORD = 'console' +CONSOLE_USERNAME = 'console' +CONSOLE_PASSWORD = 'console' HA_VIP = '' -#NAMESERVERS = '10.145.88.211' -SEARCH_PATH = ['ods.com'] -#GATEWAY = '10.145.88.1' -#PROXY = 'http://192.168.10.6:3128' -#NTP_SERVER = '10.145.88.211' MANAGEMENT_IP_START = '10.145.88.130' MANAGEMENT_IP_END = '10.145.88.254' @@ -72,25 +63,28 @@ STORAGE_PROMISC = 0 HOME_PERCENTAGE = 5 TMP_PERCENTAGE = 5 VAR_PERCENTAGE = 10 -#ROLES_LIST = [['os-dashboard']] HOST_OS = 'CentOS-6.5-x86_64' -LANGUAGE = 'EN' -TIMEZONE = 'GMT -7:00' -HTTPS_PROXY = 'https://10.145.89.100:3128' -NO_PROXY = ['127.0.0.1'] -DNS_SERVER = '10.145.89.100' -DOMAIN = 'ods.com' PRESET_VALUES = { + 'LANGUAGE': 'EN', + 'TIMEZONE': 'GMT', + 'HTTPS_PROXY': 'http://10.145.89.100:3128', + 'NO_PROXY': ['127.0.0.1'], + 'DOMAIN': 'ods.com', 'NAMESERVERS': ['10.145.89.100'], 'NTP_SERVER': '10.145.89.100', 'GATEWAY': '10.145.88.1', 'PROXY': 'http://10.145.89.100:3128', - 'FLAVOR': 'allinone', + 'OS_NAME_PATTERN': 'CentOS.*', + 'DISTRIBUTED_SYSTEM_NAME_PATTERN': 'openstack.*', + 'FLAVOR_PATTERN': 'allinone.*', 'ROLES_LIST': ['allinone-compute'], 'MACHINES_TO_ADD': ['00:0c:29:a7:ea:4b'], - 'BUILD_TIMEOUT': 60 + 'BUILD_TIMEOUT': 60, + 'SEARCH_PATH': ['ods.com'], + 'SERVER_USERNAME': 'root', + 'SERVER_PASSWORD': 'root' } for v in PRESET_VALUES: if v in os.environ.keys(): @@ -104,6 +98,10 @@ client = Client(COMPASS_SERVER_URL) # login status, token = client.login(COMPASS_LOGIN_EMAIL, COMPASS_LOGIN_PASSWORD) +print '============================================================' +print 'login status: %s token: %s' % (status, token) +if status >= 400: + sys.exit(1) # list all switches status, response = client.list_switches() @@ -125,6 +123,10 @@ if status < 400: switch = response else: status, response = client.list_switches() + print '=========================================' + print 'list switches status %s response %s' % (status, response) + if status >= 400: + sys.exit(1) for switch_ in response: if switch_['ip'] == SWITCH_IP: switch = switch_ @@ -140,10 +142,12 @@ while switch['state'] != 'under_monitoring': print 'waiting for state to become under_monitoring' client.poll_switch(switch_id) status, resp = client.get_switch(switch_id) + print '=====================================' + print 'poll switch status %s response %s' % (status, resp) switch = resp print 'switch is in state: %s' % switch['state'] time.sleep(5) -status, response = client.poll_switch(switch_id) + print '=========================================' print 'switch state now is %s' % (switch['state']) @@ -152,63 +156,59 @@ machine_macs = {} machines = {} for machine in PRESET_VALUES['MACHINES_TO_ADD']: status, response = client.list_machines(mac=machine) + print '============================================' + print 'list machines status %s response %s' % (status, response) + if status >= 400: + sys.exit(1) if status == 200 and response != []: - id = response[0]['id'] - machine_macs[id] = response[0]['mac'] + machine_id = response[0]['id'] + machine_macs[machine_id] = response[0]['mac'] machines = response print '=================================' print 'found machines are : %s' % machines -MACHINES_TO_ADD = PRESET_VALUES['MACHINES_TO_ADD'] -if set(machine_macs.values()) != set(MACHINES_TO_ADD): +machines_to_add = PRESET_VALUES['MACHINES_TO_ADD'] +if set(machine_macs.values()) != set(machines_to_add): print 'only found macs %s while expected are %s' % ( - machine_macs.values(), MACHINES_TO_ADD) + machine_macs.values(), machines_to_add) sys.exit(1) # list all adapters status, response = client.list_adapters() print '===============================' print 'all adapters are: %s' % response +if status >= 400: + sys.exit(1) + adapters = response -adapter_ids = [] -for adapter in adapters: - adapter_ids.append(adapter['id']) - -adapter_id = adapter_ids[0] -adapter = adapters[adapter_id] -print '==========================' -print 'using adapter %s to deploy cluster' % adapter_id - -# get all supported oses -supported_oses = adapter['supported_oses'] - -# get os_id +adapter_id = None os_id = None -os_name = None -for supported_os in supported_oses: - if HOST_OS in supported_os.values(): - os_id = supported_os['os_id'] - os_name = supported_os['name'] - break - -print '====================================' -print 'use %s as host os, the os_id is %s' % (os_name, os_id) - -# get flavor_id flavor_id = None -flavors = adapter['flavors'] -print '==============================' -print 'all flavors are: %s' % flavors +adapter_pattern = re.compile(PRESET_VALUES['DISTRIBUTED_SYSTEM_NAME_PATTERN']) +os_pattern = re.compile(PRESET_VALUES['OS_NAME_PATTERN']) +flavor_pattern = re.compile(PRESET_VALUES['FLAVOR_PATTERN']) +for adapter in adapters: + if ( + 'distributed_system_name' in adapter and + adapter_pattern.match(adapter['distributed_system_name']) + ): + adapter_id = adapter['id'] + for supported_os in adapter['supported_oses']: + if os_pattern.match(supported_os['name']): + os_id = supported_os['id'] + break + for flavor in adapter['flavors']: + if flavor_pattern.match(flavor['name']): + flavor_id = flavor['id'] -for flavor in flavors: - if flavor['name'] == PRESET_VALUES['FLAVOR']: - flavor_id = flavor['id'] + if adapter_id and os_id and flavor_id: break -print '====================================' -print 'cluster info: adapter_id: %s, os_id: %s, flavor_id: %s' % ( - adapter_id, os_id, flavor_id) +print '=======================================================' +print 'using adapter %s os %s flavor %s to deploy cluster' % ( + adapter_id, os_id, flavor_id +) # add a cluster status, response = client.add_cluster( @@ -217,14 +217,18 @@ status, response = client.add_cluster( os_id, flavor_id ) +print '===============================================================' print 'add cluster %s status %s: %s' % (CLUSTER_NAME, status, response) -if status < 400: - cluster = response -else: - status, response = client.list_clusters(name=CLUSTER_NAME) - print 'list clusters status %s: %s' % (status, response) - cluster = response[0] - print 'cluster already exists, fetching it' +if status >= 400: + sys.exit(1) + +status, response = client.list_clusters(name=CLUSTER_NAME) +print '================================================================' +print 'list clusters %s status %s: %s' % (CLUSTER_NAME, status, response) +if status >= 400: + sys.exit(1) + +cluster = response[0] cluster_id = cluster['id'] print '==================' @@ -244,7 +248,10 @@ status, response = client.add_hosts_to_cluster( cluster_id, machines_dict ) print '===================================' -print 'add hosts %s to cluster: %s' % (machines_dict, response) +print 'add hosts %s to cluster status %s response %s' % ( + machines_dict, status, response) +if status >= 400: + sys.exit(1) # Add two subnets subnet_1 = '10.145.89.0/24' @@ -252,14 +259,28 @@ subnet_2 = '192.168.100.0/24' status, response = client.add_subnet(subnet_1) print '==================' -print 'add subnet %s' % response +print 'add subnet %s status %s: %s' % (subnet_1, status, response) +if status >= 400: + sys.exit(1) status, response = client.add_subnet(subnet_2) print '==================' -print 'add subnet %s' % response +print 'add subnet %s status %s: %s' % (subnet_2, status, response) +if status >= 400: + sys.exit(1) status, subnet1 = client.list_subnets(subnet=subnet_1) +print '===========================================================' +print 'list subnet %s status %s: %s' % (subnet_1, status, subnet1) +if status >= 400: + sys.exit(1) + status, subnet2 = client.list_subnets(subnet=subnet_2) +print '===========================================================' +print 'list subnet %s status %s: %s' % (subnet_2, status, subnet2) +if status >= 400: + sys.exit(1) + subnet1_id = subnet1[0]['id'] subnet2_id = subnet2[0]['id'] print '========================' @@ -268,6 +289,11 @@ print 'subnet2 has id: %s, subnet is %s' % (subnet2_id, subnet2) # Add host network status, response = client.list_cluster_hosts(cluster_id) +print '================================================' +print 'list cluster hosts status %s: %s' % (status, response) +if status >= 400: + sys.exit(1) + host = response[0] host_id = host['id'] print '==================' @@ -281,7 +307,9 @@ status, response = client.add_host_network( is_mgmt=True ) print '=======================' -print 'add eth0 network: %s' % response +print 'add eth0 network status %s: %s' % (status, response) +if status >= 400: + sys.exit(1) status, response = client.add_host_network( host_id, @@ -291,25 +319,27 @@ status, response = client.add_host_network( is_promiscuous=True ) print '=======================' -print 'add eth1 network: %s' % response +print 'add eth1 network status %s: %s' % (status, response) +if status >= 400: + sys.exit(1) # Update os config to cluster cluster_os_config = { 'general': { - 'language': LANGUAGE, - 'timezone': TIMEZONE, + 'language': PRESET_VALUES['LANGUAGE'], + 'timezone': PRESET_VALUES['TIMEZONE'], 'http_proxy': PRESET_VALUES['PROXY'], - 'https_proxy': HTTPS_PROXY, - 'no_proxy': NO_PROXY, + 'https_proxy': PRESET_VALUES['HTTPS_PROXY'], + 'no_proxy': PRESET_VALUES['NO_PROXY'], 'ntp_server': PRESET_VALUES['NTP_SERVER'], 'dns_servers': PRESET_VALUES['NAMESERVERS'], - 'domain': DOMAIN, - 'search_path': SEARCH_PATH, + 'domain': PRESET_VALUES['DOMAIN'], + 'search_path': PRESET_VALUES['SEARCH_PATH'], 'default_gateway': PRESET_VALUES['GATEWAY'] }, 'server_credentials': { - 'username': SERVER_USERNAME, - 'password': SERVER_PASSWORD + 'username': PRESET_VALUES['SERVER_USERNAME'], + 'password': PRESET_VALUES['SERVER_PASSWORD'] }, 'partition': { '/var': { @@ -323,9 +353,8 @@ cluster_os_config = { cluster_package_config = { - 'roles': PRESET_VALUES['ROLES_LIST'], 'security': { - 'service_credential': { + 'service_credentials': { 'image': { 'username': SERVICE_USERNAME, 'password': SERVICE_PASSWORD @@ -359,9 +388,39 @@ cluster_package_config = { 'password': SERVICE_PASSWORD } }, - 'dashboard_credential': { - 'username': DASHBOARD_USERNAME, - 'password': DASHBOARD_PASSWORD + 'console_credentials': { + 'admin': { + 'username': CONSOLE_USERNAME, + 'password': CONSOLE_PASSWORD + }, + 'compute': { + 'username': CONSOLE_USERNAME, + 'password': CONSOLE_PASSWORD + }, + 'dashboard': { + 'username': CONSOLE_USERNAME, + 'password': CONSOLE_PASSWORD + }, + 'image': { + 'username': CONSOLE_USERNAME, + 'password': CONSOLE_PASSWORD + }, + 'metering': { + 'username': CONSOLE_USERNAME, + 'password': CONSOLE_PASSWORD + }, + 'network': { + 'username': CONSOLE_USERNAME, + 'password': CONSOLE_PASSWORD + }, + 'object-store': { + 'username': CONSOLE_USERNAME, + 'password': CONSOLE_PASSWORD + }, + 'volume': { + 'username': CONSOLE_USERNAME, + 'password': CONSOLE_PASSWORD + } } }, 'network_mapping': { @@ -379,15 +438,30 @@ status, response = client.update_cluster_config( ) print '=======================================' -print 'cluster %s has been updated to: %s' % (cluster_id, response) +print 'cluster %s update status %s: %s' % ( + cluster_id, status, response) +if status >= 400: + sys.exit(1) + +status, response = client.update_cluster_host( + cluster_id, host_id, roles=PRESET_VALUES['ROLES_LIST']) +print '=================================================' +print 'update cluster host %s/%s status %s: %s' % ( + cluster_id, host_id, status, response) +if status >= 400: + sys.exit(1) # Review and deploy status, response = client.review_cluster( cluster_id, review={'hosts': [host_id]}) print '=======================================' print 'reviewing cluster status %s: %s' % (status, response) +if status >= 400: + sys.exit(1) status, response = client.deploy_cluster( cluster_id, deploy={'hosts': [host_id]}) print '=======================================' print 'deploy cluster status %s: %s' % (status, response) +if status >= 400: + sys.exit(1) diff --git a/compass/apiclient/restful.py b/compass/apiclient/restful.py index ff6da037..f4576db8 100644 --- a/compass/apiclient/restful.py +++ b/compass/apiclient/restful.py @@ -592,36 +592,36 @@ class Client(object): def get_cluster_metadata(self, cluster_id): return self._get('/clusters/%s/metadata' % cluster_id) - def update_cluster_config(self, cluster_id, put_os_config=None, - put_package_config=None, config_step=None, + def update_cluster_config(self, cluster_id, os_config=None, + package_config=None, config_step=None, raw_data=None): data = {} if raw_data: data = raw_data - if put_os_config: - data['put_os_config'] = put_os_config + if os_config: + data['os_config'] = os_config - if put_package_config: - data['put_package_config'] = put_package_config + if package_config: + data['package_config'] = package_config if config_step: data['config_step'] = config_step return self._put('/clusters/%s/config' % cluster_id, data=data) - def patch_cluster_config(self, cluster_id, patched_os_config=None, - patched_package_config=None, config_step=None, + def patch_cluster_config(self, cluster_id, os_config=None, + package_config=None, config_step=None, raw_data=None): data = {} if raw_data: data = raw_data - if patched_os_config: - data['patched_os_config'] = patched_os_config + if os_config: + data['os_config'] = os_config - if patched_package_config: - data['patched_package_config'] = patched_package_config + if package_config: + data['package_config'] = package_config if config_step: data['config_step'] = config_step @@ -702,68 +702,68 @@ class Client(object): return self._get('/clusterhosts/%s/config' % clusterhost_id) def update_cluster_host_config(self, cluster_id, host_id, - put_os_config=None, - put_package_config=None, + os_config=None, + package_config=None, raw_data=None): data = {} if raw_data: data = raw_data else: - if put_os_config: - data['put_os_config'] = put_os_config + if os_config: + data['os_config'] = os_config - if put_package_config: - data['put_package_config'] = put_package_config + if package_config: + data['package_config'] = package_config return self._put('/clusters/%s/hosts/%s/config' % (cluster_id, host_id), data=data) - def update_clusterhost_config(self, clusterhost_id, put_os_config=None, - put_package_config=None, raw_data=None): + def update_clusterhost_config(self, clusterhost_id, os_config=None, + package_config=None, raw_data=None): data = {} if raw_data: data = raw_data else: - if put_os_config: - data['put_os_config'] = put_os_config + if os_config: + data['os_config'] = os_config - if put_package_config: - data['put_package_config'] = put_package_config + if package_config: + data['package_config'] = package_config return self._put('/clusterhosts/%s/config' % clusterhost_id, data=data) def patch_cluster_host_config(self, cluster_id, host_id, - patched_os_config=None, - patched_package_config=None, + os_config=None, + package_config=None, raw_data=None): data = {} if raw_data: data = raw_data else: - if patched_os_config: - data['patched_os_config'] = patched_os_config + if os_config: + data['os_config'] = os_config - if patched_package_config: - data['patched_package_config'] = patched_package_config + if package_config: + data['package_config'] = package_config return self._patch('/clusters/%s/hosts/%s/config' % (cluster_id, host_id), data=data) - def patch_clusterhost_config(self, clusterhost_id, patched_os_config=None, - patched_package_config=None, raw_data=None): + def patch_clusterhost_config(self, clusterhost_id, os_config=None, + package_config=None, raw_data=None): data = {} if raw_data: data = raw_data else: - if patched_os_config: - data['patched_os_config'] = patched_os_config + if os_config: + data['os_config'] = os_config - if patched_package_config: - data['patched_package_config'] = patched_package_config + if package_config: + data['package_config'] = package_config return self._patch('/clusterhosts/%s' % clusterhost_id, data=data) @@ -778,6 +778,52 @@ class Client(object): return self._get('/clusters/%s/hosts/%s/state' % (cluster_id, host_id)) + def update_cluster_host(self, cluster_id, host_id, + roles=None, raw_data=None): + data = {} + if raw_data: + data = raw_data + else: + if roles: + data['roles'] = roles + + return self._put('/clusters/%s/hosts/%s' % + (cluster_id, host_id), data=data) + + def update_clusterhost(self, clusterhost_id, + roles=None, raw_data=None): + data = {} + if raw_data: + data = raw_data + else: + if roles: + data['roles'] = roles + + return self._put('/clusterhosts/%s' % clusterhost_id, data=data) + + def patch_cluster_host(self, cluster_id, host_id, + roles=None, raw_data=None): + data = {} + if raw_data: + data = raw_data + else: + if roles: + data['roles'] = roles + + return self._patch('/clusters/%s/hosts/%s' % + (cluster_id, host_id), data=data) + + def patch_clusterhost(self, clusterhost_id, + roles=None, raw_data=None): + data = {} + if raw_data: + data = raw_data + else: + if roles: + data['roles'] = roles + + return self._patch('/clusterhosts/%s' % clusterhost_id, data=data) + def get_clusterhost_state(self, clusterhost_id): return self._get('/clusterhosts/%s/state' % clusterhost_id) @@ -884,17 +930,17 @@ class Client(object): def get_host_config(self, host_id): return self._get('/hosts/%s/config' % host_id) - def update_host_config(self, host_id, put_os_config, raw_data=None): + def update_host_config(self, host_id, os_config, raw_data=None): data = {} - data['put_os_config'] = put_os_config + data['os_config'] = os_config if raw_data: data.update(raw_data) return self._put('/hosts/%s/config' % host_id, data=data) - def patch_host_config(self, host_id, patched_os_config, raw_data=None): + def patch_host_config(self, host_id, os_config, raw_data=None): data = {} - data['patched_os_config'] = patched_os_config + data['os_config'] = os_config if raw_data: data.update(raw_data)