def __init__(self, hostname, username='******', password=None, keypath=None, region_domain=None, proxy_hostname=None, proxy_username=None, proxy_password=None, proxy_keypath=None, config_yml=None, config_qa=None, credpath=None, aws_access_key=None, aws_secret_key=None, log_level='INFO', boto_debug_level=0, euca_user='******', euca_account='eucalyptus', https=True): self.machine_connect_kwargs = { 'hostname': hostname, 'username': username, 'password': password, 'keypath': keypath, 'proxy_hostname': proxy_hostname, 'proxy_username': proxy_username or username, 'proxy_password': proxy_password or password, 'proxy_keypath': proxy_keypath or keypath, 'log_level': log_level } self._clc_machine = None self.hostname = hostname self.config_qa = config_qa self.config_yml = config_yml # self._aws_access_key = aws_access_key # self._aws_secret_key = aws_secret_key self._eucahosts = {} self._credpath = credpath self.log = Eulogger(identifier=self.__class__.__name__, stdout_level=log_level) self.creds = AutoCreds(credpath=self._credpath, region_domain=region_domain, aws_access_key=aws_access_key, aws_secret_key=aws_secret_key, aws_account_name=euca_account, aws_user_name=euca_user, https=https, logger=self.log, **self.machine_connect_kwargs) super(SystemConnection, self).__init__(hostname=hostname, aws_secret_key=self.creds.aws_secret_key, aws_access_key=self.creds.aws_access_key, logger=self.log, boto_debug_level=boto_debug_level)
def __init__(self, access_key=None, secret_key=None, account_id=None, region_domain=None, s3_url=None, ec2_url=None, bootstrap_url=None, ec2_cert_path=None, worker_hostname=None, worker_keypath=None, worker_username='******', worker_password=None, worker_machine=None, user_context=None, log_level='debug', destpath=None, time_per_gig=300, eof=True): self.access_key = access_key self.secret_key = secret_key self.account_id = account_id self.region_domain = region_domain self.bootstrap_url = bootstrap_url self.s3_url = s3_url self.ec2_url = ec2_url self.ec2_cert_path = ec2_cert_path self.log = Eulogger('Euca2oolsImageUtils', stdout_level=log_level) # Setup the work machine, this is the machine which will be used for # performing the 'work' (download, bundle, etc) self.worker_hostname = worker_hostname self.worker_keypath = worker_keypath self.worker_username = worker_username self.worker_password = worker_password self._worker_machine = None self._user_context = user_context if worker_machine: self._worker_machine = worker_machine self.time_per_gig = time_per_gig self.eof = eof if destpath is not None: self.destpath = str(destpath) else: self.destpath = "/disk1/storage" self.time_per_gig = time_per_gig
def __init__(self, xml_element, eucanetd=None, log_level='INFO'): if xml_element is not None and not isinstance(xml_element, Element): raise ValueError('xml_element must be of type: {0}.{1}'.format( Element.__module__, Element.__name__)) self._xml = xml_element self._eucanetd = eucanetd self._tag = None self._name = None self._log = Eulogger("{0}:{1}:{2}".format(self.__class__.__name__, self.tag or "", self.name or ""), stdout_level=log_level) self._update_from_xml(xml=self._xml)
def __init__(self, connection=None): super(ConversionTask, self).__init__(connection) self.connection = connection self.conversiontaskid = None self.expirationtime = None self.state = None self.statusmessage = None self._importinstancetask = None self._importvolumetask = None self._instance = None self._snapshots = None self.tags = None self.notfound = False self.log = Eulogger('ConversionTask:{0}'.format(self.id))
def enable_connection_debug(self, level=DEBUG, format_string=None): try: level = Eulogger.format_log_level(level, 'DEBUG') set_stream_logger('botocore', level=level, format_string=None) except: self.log.error('Could not enable debug for: "{0}"'.format(self)) raise
def __init__(self, hostfile=None, ips=None, password=None, keypath=None, username='******', command='echo "ALIVE', timeout=5, no_pty=False, thread_count=20, log_level='debug'): self.parser = argparse.ArgumentParser( description='Run a command on list of remote hosts', formatter_class=argparse.ArgumentDefaultsHelpFormatter) self.parser.add_argument('-f', '--hostfile', default=hostfile, help='file with list of ips and/or hostnames') self.parser.add_argument('-i', '--ips', default=ips, help='comma or space separated list of ips and/or hostnames') self.parser.add_argument('-p', '--password', default=password, help='Ssh password used to connect to hosts') self.parser.add_argument('-k', '--keypath', default=keypath, help='Local path to specific ssh key used to connect to hosts') self.parser.add_argument('-u', '--username', default=username, help='Ssh username used to connect to hosts') self.parser.add_argument('-c', '--command', default=command, help='file with list of ips and/or hostnames') self.parser.add_argument('-t', '--timeout', default=timeout, type=int, help='Ssh connection timeout in seconds') self.parser.add_argument('-b', '--batch-timeout', default=0, type=int, help='Timeout for sum of all tasks to complete in seconds. ' 'This includes time to create all remote ' 'connections + execute commands') self.parser.add_argument('--thread-count', default=thread_count, type=int, help='Number of threads used to run commands on hosts') self.parser.add_argument('--no-pty', default=no_pty, action='store_false', help='Do not request a pseudo-terminal from the server.') self.parser.add_argument('-l', '--log-level', default=log_level, help='Loglevel') if ips or hostfile: args = "" else: args = None self.args = self.parser.parse_args(args=args) self.hostfile = self.args.hostfile self.password = self.args.password self.keypath = self.args.keypath self.username = self.args.username self.command = self.args.command self.timeout = self.args.timeout self.log_level = self.args.log_level self.results = {} self.maxwait = .5 self.ips = ips or self.args.ips or [] self.logger = Eulogger('RemoteCmds', stdout_level=self.log_level) if self.ips: if isinstance(self.ips, basestring): self.ips = str(self.ips).replace(',', ' ') self.ips = self.ips.split() else: self.ips = list(self.ips) if self.args.hostfile: with open(os.path.expanduser(self.args.hostfile)) as f: self.ips.extend(f.readlines()) if not self.ips: raise ValueError('No hosts provided. Use --hostfile or --ips to provide hosts to run ' 'command against')
def disable_boto3_connection_debug(self, level=NOTSET): try: self.connection.debug = 0 level = Eulogger.format_log_level(level, 'NOTSET') b3_set_stream_logger('botocore', level=level, format_string=None) except: self.log.error('Could not disable debug for: "{0}"'.format(self)) raise
def __init__(self, host=None, password=None, keypath=None, sshconnection=None, machine=None, eucalyptus_run_path='/var/run/eucalyptus', log_level='INFO'): if (machine and (sshconnection or host)) or sshconnection and host: warning = 'Duplicate and or possibly conflicting machine connection info provided:' \ 'host:{0}, sshconnection:{1}, machine:{2}'.format(host, sshconnection, machine) else: warning = "" if machine: sshconnection = machine.ssh if sshconnection: host = host or sshconnection.host password = password or sshconnection.password keypath = sshconnection or sshconnection.keypair if host: if not machine: machine = Machine(hostname=host, password=password, keypath=keypath, sshconnection=sshconnection) host = host or "unknown" self.log = Eulogger("{0}.{1}".format(self.__class__.__name__, host)) self.log.set_stdout_loglevel(log_level) if not host: self.log.warning( 'Connection info not provided for: {0}.init()'.format( self.__class__.__name__)) self.host = host self.password = password self.keypath = keypath self._machine = machine self._global_xml_path = None self._global_xml = None self.eucalyptus_run_path = eucalyptus_run_path or "" self.eucanetd_pid_file = path.join(self.eucalyptus_run_path, 'eucanetd.pid') self.global_xml_version = path.join(self.eucalyptus_run_path, 'global_network_info.version')
def disable_connection_debug(self, level=NOTSET): try: self.connection.debug = 0 level = Eulogger.format_log_level(level, 'NOTSET') set_stream_logger('botocore', level=level, format_string=None) except: self.log.error('Could not disable debug for: "{0}"'.format(self)) raise
def __init__(self, creds=None, host=None, aws_access_key=None, aws_secret_key=None, is_secure=None, port=None, path=None, logger=None, boto_debug=0, **kwargs): self.debug = boto_debug if self.debug: set_stream_logger('boto') if creds and not isinstance(creds, Eucarc): credsattr = getattr(creds, 'creds', None) if not isinstance(credsattr, Eucarc): raise ValueError( 'Unknown type passed for creds arg: "{0}/{1}"'.format( creds, type(creds))) creds = credsattr self._eucarc = creds self.account_id = None self.user_id = None if not logger: logger = Eulogger(identifier=self.__class__.__name__) self.log = logger if creds: self.user_id = creds.ec2_user_id self.account_id = creds.ec2_account_number assert isinstance(creds, Eucarc), 'UserAdmin. eucarc not type Eucarc(), got:"{0}/{1}"'\ .format(creds, type(creds)) urlp = urlparse(creds.euare_url) host = host or getattr(urlp, 'hostname', None) port = port or getattr(urlp, 'port', 8773) path = path or getattr(urlp, 'path', '/services/Euare') if is_secure is None and urlp.scheme == 'https': is_secure = True aws_secret_key = aws_secret_key or creds.aws_secret_key aws_access_key = aws_access_key or creds.aws_access_key is_secure = is_secure or False ac_kwargs = { 'host': host, 'aws_access_key_id': aws_access_key, 'aws_secret_access_key': aws_secret_key, 'is_secure': is_secure, 'port': port, 'path': path } ac_kwargs.update(kwargs) try: super(AccessConnection, self).__init__(**ac_kwargs) except: self.log.error( 'Failed to create AccessConnection with kwargs:"{0}"'.format( ac_kwargs)) raise
def __init__( self, hostname, username="******", password=None, keypath=None, proxy_hostname=None, proxy_username="******", proxy_password=None, proxy_keypath=None, config_yml=None, config_qa=None, credpath=None, aws_access_key=None, aws_secret_key=None, log_level="INFO", boto_debug_level=0, euca_user="******", euca_account="eucalyptus", ): self.clc_connect_kwargs = { "hostname": hostname, "username": username, "password": password, "keypath": keypath, "proxy_hostname": proxy_hostname, "proxy_username": proxy_username, "proxy_password": proxy_password, "proxy_keypath": proxy_keypath, } self._clc_machine = None self.hostname = hostname self.config_qa = config_qa self.config_yml = config_yml # self._aws_access_key = aws_access_key # self._aws_secret_key = aws_secret_key self._eucahosts = {} self._credpath = credpath self.log = Eulogger(identifier=self.__class__.__name__, stdout_level=log_level) self.creds = AutoCreds( credpath=self._credpath, aws_access_key=aws_access_key, aws_secret_key=aws_secret_key, aws_account_name=euca_account, aws_user_name=euca_user, logger=self.log, **self.clc_connect_kwargs ) super(SystemConnection, self).__init__( hostname=hostname, aws_secret_key=self.creds.aws_secret_key, aws_access_key=self.creds.aws_access_key, logger=self.log, boto_debug_level=boto_debug_level, )
def set_boto_logger_level(level='NOTSET', format_string=None): """ Set the global boto loggers levels to 'level'. ie "DEBUG", "INFO", "CRITICAL" default is "NOTSET" :param level: string matching logging class levels, or integer representing the equiv value :param format_string: logging class formatter string """ level = Eulogger.format_log_level(level, 'NOTSET') set_stream_logger('boto', level=level, format_string=None) set_stream_logger('boto3', level=level, format_string=None) set_stream_logger('botocore', level=level, format_string=None)
def __init__(self, hostname, username='******', password=None, keypath=None, region_domain=None, proxy_hostname=None, proxy_username=None, proxy_password=None, proxy_keypath=None, config_yml=None, config_qa=None, credpath=None, aws_access_key=None, aws_secret_key=None, log_level='INFO', boto_debug_level=0, euca_user='******', euca_account='eucalyptus', ): self.machine_connect_kwargs = { 'hostname': hostname, 'username': username, 'password': password, 'keypath': keypath, 'proxy_hostname': proxy_hostname, 'proxy_username': proxy_username or username, 'proxy_password': proxy_password or password, 'proxy_keypath': proxy_keypath or keypath, 'log_level': log_level } self._clc_machine = None self.hostname = hostname self.config_qa = config_qa self.config_yml = config_yml # self._aws_access_key = aws_access_key # self._aws_secret_key = aws_secret_key self._eucahosts = {} self._credpath = credpath self.log = Eulogger(identifier=self.__class__.__name__, stdout_level=log_level) self.creds = AutoCreds(credpath=self._credpath, region_domain=region_domain, aws_access_key=aws_access_key, aws_secret_key=aws_secret_key, aws_account_name=euca_account, aws_user_name=euca_user, logger=self.log, **self.machine_connect_kwargs) super(SystemConnection, self).__init__(hostname=hostname, aws_secret_key=self.creds.aws_secret_key, aws_access_key=self.creds.aws_access_key, logger=self.log, boto_debug_level=boto_debug_level)
def __init__(self, hostfile=None, ips=None, password=None, username='******', command='echo "ALIVE', timeout=5, thread_count=20, log_level='debug'): self.parser = argparse.ArgumentParser( description='Run a command on list of remote hosts', formatter_class=argparse.ArgumentDefaultsHelpFormatter) self.parser.add_argument('-f', '--hostfile', default=hostfile, help='file with list of ips and/or hostnames') self.parser.add_argument('-i', '--ips', default=ips, help='comma or space separated list of ips and/or hostnames') self.parser.add_argument('-p', '--password', default=password, help='Ssh password used to connect to hosts') self.parser.add_argument('-u', '--username', default=username, help='Ssh username used to connect to hosts') self.parser.add_argument('-c', '--command', default=command, help='file with list of ips and/or hostnames') self.parser.add_argument('-t', '--timeout', default=timeout, type=int, help='Ssh connection timeout in seconds') self.parser.add_argument('--thread-count', default=thread_count, type=int, help='Number of threads used to run commands on hosts') self.parser.add_argument('-l', '--log-level', default=log_level, help='Loglevel') if ips or hostfile: args = "" else: args = None self.args = self.parser.parse_args(args=args) self.hostfile = self.args.hostfile self.password = self.args.password self.username = self.args.username self.command = self.args.command self.timeout = self.args.timeout self.log_level = self.args.log_level self.results = {} self.maxwait = .5 self.ips = ips or self.args.ips or [] self.logger = Eulogger('RemoteCmds', stdout_level=self.log_level) if self.ips: if isinstance(self.ips, basestring): self.ips = str(self.ips).replace(',', ' ') self.ips = self.ips.split() else: self.ips = list(self.ips) if self.args.hostfile: with open(os.path.expanduser(self.args.hostfile)) as f: self.ips.extend(f.readlines()) if not self.ips: raise ValueError('No hosts provided. Use --hostfile or --ips to provide hosts to run ' 'command against')
class SystemConnection(ServiceConnection): def __init__(self, hostname, username='******', password=None, keypath=None, region_domain=None, proxy_hostname=None, proxy_username=None, proxy_password=None, proxy_keypath=None, config_yml=None, config_qa=None, credpath=None, aws_access_key=None, aws_secret_key=None, log_level='INFO', boto_debug_level=0, euca_user='******', euca_account='eucalyptus', https=True): self.machine_connect_kwargs = { 'hostname': hostname, 'username': username, 'password': password, 'keypath': keypath, 'proxy_hostname': proxy_hostname, 'proxy_username': proxy_username or username, 'proxy_password': proxy_password or password, 'proxy_keypath': proxy_keypath or keypath, 'log_level': log_level } self._clc_machine = None self.hostname = hostname self.config_qa = config_qa self.config_yml = config_yml # self._aws_access_key = aws_access_key # self._aws_secret_key = aws_secret_key self._eucahosts = {} self._credpath = credpath self.log = Eulogger(identifier=self.__class__.__name__, stdout_level=log_level) self.creds = AutoCreds(credpath=self._credpath, region_domain=region_domain, aws_access_key=aws_access_key, aws_secret_key=aws_secret_key, aws_account_name=euca_account, aws_user_name=euca_user, https=https, logger=self.log, **self.machine_connect_kwargs) super(SystemConnection, self).__init__(hostname=hostname, aws_secret_key=self.creds.aws_secret_key, aws_access_key=self.creds.aws_access_key, logger=self.log, boto_debug_level=boto_debug_level) def set_loglevel(self, level, parent=False): """ wrapper for log.setLevel, accept int or string. Levels can be found in logging class. At the time this was written they are: CRITICAL:50 DEBUG:10 ERROR:40 FATAL:50 INFO:20 NOTSET:0 WARN:30 WARNING:30 """ level = level or logging.NOTSET if not isinstance(level, int) and not isinstance(level, basestring): raise ValueError( 'set_loglevel. Level must be of type int or string, got: "{0}/{1}"' .format(level, type(level))) if isinstance(level, basestring): level = getattr(logging, str(level).upper()) return self.log.set_parentloglevel(level) @property def clc_machine(self): if not self._clc_machine: hostname = self.machine_connect_kwargs['hostname'] if hostname: # See if a host exists matching the provided hostname if hostname in self.eucahosts: self._clc_machine = self.eucahosts[hostname] # See if this is a localhost connection elif self._get_clc_eucahost_for_localhost(): self._clc_machine = self._get_clc_eucahost_for_localhost() else: self._clc_machine = Machine(**self.machine_connect_kwargs) self.eucahosts[self.machine_connect_kwargs[ 'hostname']] = self._clc_machine return self._clc_machine @property def eucahosts(self): if not self._eucahosts: self._eucahosts = self._update_host_list() return self._eucahosts def _get_clc_eucahost_for_localhost(self): ifaces = self._get_all_local_ip_interfaces() for iface, ip in ifaces: if ip in self.eucahosts: self.log.debug('CLC is bound to iface:{0} ip:{1}'.format( iface, ip)) return self.eucahosts[ip] return None def _get_all_local_ip_interfaces(self): max_possible = 1028 bytes = max_possible * 32 s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) names = array.array('B', '\0' * bytes) outbytes = struct.unpack( 'iL', fcntl.ioctl( s.fileno(), 0x8912, # SIOCGIFCONF struct.pack('iL', bytes, names.buffer_info()[0])))[0] namestr = names.tostring() interfaces = [] for i in range(0, outbytes, 40): name = namestr[i:i + 16].split('\0', 1)[0] addr = namestr[i + 20:i + 24] ip = "{0}.{1}.{2}.{3}".format(ord(addr[0]), ord(addr[1]), ord(addr[2]), ord(addr[3])) interfaces.append((name, ip)) return interfaces def _update_host_list(self): machines = self.get_all_machine_mappings() connect_kwargs = copy.copy(self.machine_connect_kwargs) if 'hostname' in connect_kwargs: connect_kwargs.pop('hostname') hostlock = threading.Lock() def add_host(ip, services, self=self, connect_kwargs=connect_kwargs): host = EucaHost(connection=self, hostname=ip, services=services, **connect_kwargs) with hostlock: self._eucahosts[ip] = host threads = [] for ip, services in machines.iteritems(): t = threading.Thread(target=add_host, args=(ip, services)) t.start() threads.append(t) for t in threads: t.join() return self._eucahosts def get_host_by_hostname(self, hostname): return self.eucahosts.get(hostname, None) def get_hosts_by_service_type(self, servicetype): ret_list = [] for ip, host in self.eucahosts.iteritems(): for service in host.services: if service.type == servicetype: ret_list.append(host) return ret_list def get_hosts_for_cloud_controllers(self): clc = None return self.get_hosts_by_service_type(servicetype='eucalyptus') def get_hosts_for_node_controllers(self, partition=None, instanceid=None): if instanceid is not None and not isinstance(instanceid, basestring): raise ValueError( 'Instance id not of string type, got:"{0}"/"{1}"'.format( instanceid, type(instanceid))) ncs = self.get_hosts_by_service_type(servicetype='node') if not partition and not instanceid: return ncs retlist = [] if instanceid: try: reservation = self.ec2_connection.get_all_instances( instance_ids=['verbose', instanceid]) except: self.log.error( '{0}\nFailed to find instance:"{1}" on system'.format( get_traceback(), instanceid)) return [] if reservation: instance = reservation[0].instances[0] node_addr = instance.tags.get('euca:node') if node_addr: for nc in ncs: if nc.hostname == node_addr: return [nc] if partition and partition in nc.partitions: retlist.append(nc) return retlist def get_hosts_for_cluster_controllers(self, partition=None): ccs = self.get_hosts_by_service_type(servicetype='cluster') if not partition: return ccs retlist = [] for cc in ccs: if partition in cc.partitions: retlist.append(cc) return retlist def get_hosts_for_storage_controllers(self, partition=None): scs = self.get_hosts_by_service_type(servicetype='storage') if not partition: return scs retlist = [] for sc in scs: if partition in sc.partitions: retlist.append(sc) return retlist def get_hosts_for_ufs(self): ufs = None return self.get_hosts_by_service_type(servicetype='user-api') def get_hosts_for_walrus(self): walrus = None return self.get_hosts_by_service_type(servicetype='walrusbackend') def show_cloud_legacy_summary(self, repo_info=True, print_method=None, file_path=None, print_table=True): """ Creates a table representing the legacy Eutester/QA reprsentation of a Eucalyptus cloud. This can be used for legacy eutester tests, etc.. :param repo_info: bool, if True will use the work REPO in place of Zone for the 5th column :param print_method: method used to print this table, defaults to self.log.info :param print_table: bool, if False will return the table obj :param file_path: string representing a local file path to save this information to :return: table obj if print_table is False """ ret = "" print_method = print_method or self.log.info if repo_info: rz_col = 'REPO' else: rz_col = 'ZONE' pt = PrettyTable( ['# HOST', 'DISTRO', 'VER', 'ARCH', rz_col, 'SERVICE CODES']) pt.align = 'l' pt.border = 0 for ip, host in self.eucahosts.iteritems(): split = host.summary_string.split() service_codes = " ".join(split[5:]) if repo_info: rz_col = 'REPO' else: rz_col = split[4] pt.add_row([ split[0], split[1], split[2], split[3], rz_col, service_codes ]) ret += "{0}\n".format(host.summary_string) if file_path: with open(file_path, 'w') as save_file: save_file.write(str(pt)) save_file.flush() if print_table: print_method("\n{0}\n".format(str(pt))) else: return pt @staticmethod def vm_state_markup(state): if state in ['shutting-down', 'stopped', 'stopping']: return [1, 91] if state == 'terminated': return [1, 97] if state == 'running': return [1, 92] return [1, 93] def show_hosts(self, hosts=None, partition=None, service_type=None, serv_columns=None, update=True, print_method=None, print_table=True, save_file=None): print_method = print_method or self._show_method ins_id_len = 10 ins_type_len = 13 ins_dev_len = 16 ins_st_len = 15 ins_total = (ins_id_len + ins_dev_len + ins_type_len + ins_st_len) + 5 machine_hdr = (markup('MACHINE INFO'), 30) service_hdr = (markup('EUCALYPTUS SERVICES'), 90) pt = PrettyTable([machine_hdr[0], service_hdr[0]]) pt.header = False pt.align = 'l' pt.hrules = 1 pt.max_width[machine_hdr[0]] = machine_hdr[1] total = [] eucahosts = {} if hosts is None: eucahosts = self.eucahosts elif isinstance(hosts, list): for host in hosts: eucahosts[host.hostname] = host elif isinstance(hosts, EucaHost): eucahosts[hosts.hostname] = hosts if not isinstance(eucahosts, dict): raise ValueError('show_machine_mappings requires dict example: ' '{"host ip":[host objs]}, got:"{0}/{1}"'.format( eucahosts, type(eucahosts))) # To format the tables services, print them all at once and then sort the table # rows string into the machines columns try: sorted_ips = sorted( list(eucahosts), key=lambda ip: struct.unpack("!L", socket.inet_aton(ip))[0]) except Exception as SE: self.log.warning( '"Failed to sort host list by IP, error:"{0}"'.format(SE)) sorted_ips = sorted(list(eucahosts)) for hostip in sorted_ips: host = eucahosts[hostip] for serv in host.services: if update: serv.update() total.append(serv) if serv.child_services: total.extend(serv.child_services) # Create a table showing the service states, grab the first 3 columns # for type, name, state, and zone servpt = self.show_services(total, print_table=False) # Get a subset of the show services fields... if serv_columns is None: fields = servpt._field_names[0:4] else: fields = servpt._fields_names[serv_columns] serv_lines = servpt.get_string(border=0, padding_width=2, fields=fields).splitlines() header = serv_lines[0] ansi_escape = re.compile(r'\x1b[^m]*m') # Now build the machine table... threads = [] hostlock = threading.Lock() # Method to allow host info to be gathered concurrently def add_host(hostip, host, self=self): assert isinstance(host, EucaHost) servbuf = header + "\n" mservices = [] # Get the child services (ie for UFS) for serv in host.services: mservices.append(serv) mservices.extend(serv.child_services) for serv in mservices: for line in serv_lines: # Remove the ansi markup for parsing purposes, but leave it in the # displayed line clean_line = ansi_escape.sub('', line) splitline = clean_line.split() if len(splitline) < 2: continue line_type = splitline[0] line_name = splitline[1] # Pull matching lines out of the pre-formatted service table... if (splitline and re.match("^{0}$".format(serv.type), line_type) and re.match("^{0}$".format(serv.name), line_name)): # Add this line to the services to be displayed for this machine if line_name not in servbuf: servbuf += line + "\n" if serv.type == 'node': if getattr(serv, 'instances', None): if serv.instances: vm_pt = PrettyTable([ markup('INSTANCES', [1, 4]), markup('STATE:', [1, 4]), markup('VMTYPE:', [1, 4]), markup('ROOT_DEV:', [1, 4]) ]) vm_pt.align = 'l' vm_pt.border = 1 vm_pt.vrules = 2 vm_pt.hrules = 0 for x in serv.instances: vm_pt.add_row([ x.id, markup(x.state, self.vm_state_markup(x.state)), x.instance_type, x.root_device_type ]) servbuf += "{0}\n".format(vm_pt) av_pt = host.helpers.node_controller.show_availability_for_node( print_table=False) servbuf += av_pt.get_string() ps_sum_pt = host.show_euca_process_summary(print_table=False) servbuf += "\n" + ps_sum_pt.get_string( border=1, vrules=2, hrules=0) host_info = markup('Euca Versions:').ljust(machine_hdr[1]) host_info += "Cloud: {0}".format( host.get_eucalyptus_version()).ljust(machine_hdr[1]) host_info += "2ools: {0}".format( host.get_euca2ools_version()).ljust(machine_hdr[1]) host_info += markup("Hostname:").ljust(machine_hdr[1]) host_info += str(host.hostname).ljust(machine_hdr[1]) sys_pt = host.show_sys_info(print_table=False) host_info += "{0}".format(sys_pt) with hostlock: pt.add_row([ markup("HOST:") + markup(hostip, [1, 94]), markup('EUCALYPTUS SERVICES:') + markup( '[ {0} ]'.format(" ".join( str(x) for x in host.euca_service_codes)), [1, 34]) ]) pt.add_row([host_info, servbuf]) for hostip, host in eucahosts.iteritems(): t = threading.Thread(target=add_host, args=(hostip, host)) t.start() threads.append(t) for t in threads: t.join() if save_file: with open(save_file, 'w') as sf: sf.write("\n{0}\n".format(pt.get_string())) if print_table: # print_method("\n{0}\n".format(pt.get_string(sortby=pt.field_names[1]))) print_method("\n{0}\n".format(pt.get_string())) else: return pt def upgrade_cloud(self, network_mode=None, ccs=None, ncs=None, clcs=None, scs=None, ufs=None, ws=None, gpgcheck=False, yum_arg_list=None, dry_run=False, rerun=False): if rerun: if not hasattr(self, '_upgrade_dict'): raise ValueError( 'self._upgrade_dict not found, can not use "rerun"') return self.upgrade_cloud(**self._upgrade_dict) if yum_arg_list is None: yum_arg_list = [] if not isinstance(yum_arg_list, list): yum_arg_list = [yum_arg_list] if not gpgcheck: yum_arg_list.append("--nogpg") yum_args = " -y {0}".format(" ".join(yum_arg_list)) # Sort out the host machines by services... known_net_modes = ['EDGE', 'VPCMIDO', 'MANAGED'] if network_mode is None: try: cluster_name = self.get_all_cluster_names()[0] prop = self.get_property( "{0}.cluster.networkmode".format(cluster_name)) network_mode = prop.value except: self.log.error('Could not retrieve network mode for cloud') raise if re.search('MANAGED', network_mode): network_mode = 'MANAGED' if network_mode not in known_net_modes: raise ValueError( 'Unknown network mode:{0}, known types {1}'.format( network_mode, ", ".join(known_net_modes))) # service arrays eucalyptus_cloud_hosts = [] eucanetd_hosts = [] ccs = ccs or self.get_hosts_for_cluster_controllers() ncs = ncs or self.get_hosts_for_node_controllers() clcs = clcs or self.get_hosts_for_cloud_controllers() scs = scs or self.get_hosts_for_storage_controllers() ufs = ufs or self.get_hosts_for_ufs() ws = ws or self.get_hosts_for_walrus() upgrade_dict = { 'network_mode': network_mode, 'ccs': ccs, 'ncs': ncs, 'clcs': clcs, 'scs': scs, 'ufs': ufs, 'ws': ws, 'gpgcheck': gpgcheck, 'yum_arg_list': yum_arg_list } if dry_run: return upgrade_dict for host in clcs + ufs + scs + ws: if host not in eucalyptus_cloud_hosts: eucalyptus_cloud_hosts.append(host) if network_mode == "MANAGED": eucanetd_hosts = ccs elif network_mode == 'EDGE': eucanetd_hosts = ncs elif network_mode == 'VPCMIDO': eucanetd_hosts = clcs else: raise ValueError( 'Unsupported network mode: "{0}"'.format(network_mode)) def stop_service(host, service, timeout=300): try: host.sys('service {0} stop'.format(service), code=0, timeout=timeout) except CommandExitCodeException as CE: if CE.status == 2: # service is already stopped pass else: raise try: # Shutdown all the Eucalyptus cloud services... self.log.info( 'Beginning upgrade. Shutting down all cloud services now...') for host in clcs: stop_service(host, 'eucalyptus-cloud') for host in eucalyptus_cloud_hosts: # Skip the CLCs which have already been stopped if host not in clcs: stop_service(host, 'eucalyptus-cloud') for host in ccs: stop_service(host, 'eucalyptus-cc') for host in ncs: stop_service(host, 'eucalyptus-nc') for host in eucanetd_hosts: stop_service(host, 'eucanetd') # Upgrade packages... self.log.info('Upgrading Eucalyptus packages on all hosts') for host in self.eucahosts.itervalues(): host.sys('yum upgrade eucalyptus {0}'.format(yum_args), code=0, timeout=400) self.log.info( 'Package upgrade complete, restarting cloud services now...') # Start all the Eucalyptus cloud services... # Do the CLCs first than the other Java/Cloud services self.log.info('Starting CLCs...') for host in clcs: host.sys('service eucalyptus-cloud start', code=0, timeout=300) self.log.info('Starting remaining Java Components...') for host in eucalyptus_cloud_hosts: # Skip the CLCs which have already been started if host not in clcs: host.sys('service eucalyptus-cloud start', code=0, timeout=300) self.log.info('Starting Cluster Controllers...') for host in ccs: host.sys('service eucalyptus-cc start', code=0, timeout=300) self.log.info('Starting Node Controllers...') for host in ncs: host.sys('service eucalyptus-nc start', code=0, timeout=300) self.log.info('Starting Eucanetd...') for host in eucanetd_hosts: host.sys('service eucanetd start', code=0, timeout=300) self.log.info('Upgrade Done') except: self.log.error( 'Upgrade failed. The upgrade params are found in self._upgrade_dict.' 'These can be used via the "rerun" argument to rerun this upgrade' 'using the same environment/machines') raise finally: # write to this dict for before/after comparison of the cloud after upgrade self._upgrade_dict = upgrade_dict def build_machine_dict_from_config(cls): raise NotImplementedError() def build_machine_dict_from_cloud_services(self): raise NotImplementedError('not yet implemented')
def tag(self, tag): if tag != self.tag: self._tag = tag self._log = Eulogger("{0}:{1}:{2}".format(self.__class__.__name__, self.tag or "", self.name or ""))
def __init__(self, aws_access_key=None, aws_secret_key=None, aws_account_name=None, aws_user_name=None, port=8773, credpath=None, string=None, region=None, machine=None, keysdir=None, logger=None, service_connection=None, eucarc=None, existing_certs=False, boto_debug=0, https=True, api_version=None, log_level=None): if log_level is None: if service_connection: log_level = service_connection.log.stdout_level else: log_level = DEBUG super(UserContext, self).__init__(aws_access_key=aws_access_key, aws_secret_key=aws_secret_key, aws_account_name=aws_account_name, aws_user_name=aws_user_name, service_port=port, region_domain=region, credpath=credpath, string=string, machine=machine, keysdir=keysdir, logger=logger, log_level=log_level, existing_certs=existing_certs, service_connection=service_connection, https=https, auto_create=False) self._user_info = {} self._session = None self._connections = {} self.region = region self.api_version = api_version or __DEFAULT_API_VERSION__ # Logging setup if not logger: logger = Eulogger(str(self), stdout_level=log_level) self.log = logger self.log.debug = self.log.debug self.critical = self.log.critical self.info = self.log.info if eucarc: for key, value in eucarc.__dict__.iteritems(): setattr(self, key, value) elif not (aws_access_key and aws_secret_key and self.serviceconnection): try: self.auto_find_credentials(assume_admin=False) except ValueError as VE: self.log.error( 'Failed to auto create user credentials and service paths:"{0}"' .format(VE)) if service_connection: self.update_attrs_from_cloud_services() self._test_resources = {} self._connection_kwargs = { 'eucarc': self, 'connection_debug': boto_debug, 'user_context': self, 'region': region, 'api_version': self.api_version, 'log_level': log_level } self.log.identifier = str(self) self.log.debug('Successfully created User Context')
def __init__(self, eucarc=None, credpath=None, service_url=None, aws_access_key_id=None, aws_secret_access_key=None, is_secure=False, port=None, host=None, region=None, connection_debug=0, path=None, validate_certs=True, test_resources=None, logger=None, log_level=None, user_context=None, session=None, boto2_api_version=None, verbose_requests=None): if self.EUCARC_URL_NAME is None: raise NotImplementedError( 'EUCARC_URL_NAME not set for this class:"{0}"'.format( self.__class__.__name__)) if self.SERVICE_PREFIX is None: raise NotImplementedError( 'Service Prefix has not been defined for this class:"{0}"'. format(self.__class__.__name__)) init_kwargs = locals() init_kwargs.__delitem__('self') self._session = session self.service_host = None self.service_port = None self.service_path = None # Store info about created resources and how to clean/delete them for this ops connection self.test_resources_clean_methods = {} self.test_resources = test_resources or {} # Store the user context for this connection if provided self._user_context = user_context if not region and self._user_context: region = self._user_context.region self.service_region = region # Create the logger for this ops connection if log_level is None: log_level = DEBUG def get_logger_context(): host = self.service_host or "" context = "" try: if self._user_context: if self._user_context._user_name and self._user_context._account_name: context = "({0}:{1})".format( self._user_context.user_name, self._user_context.account_name) elif self._user_context.access_key: context = "(AK:{0}...)".format( self._user_context.__access_key[5:]) except Exception as LE: print 'error fetching user context: "{0}"'.format(LE) if not context: if aws_access_key_id: context = "(AK:{0}...)".format(aws_access_key_id[5:]) else: context = "()" return context if not logger: logger = Eulogger("{0}{1}".format(self.__class__.__name__, get_logger_context()), stdout_level=log_level) self.log = logger self.log.debug('Creating ops: {0}'.format(self.__class__.__name__)) self.log.set_stdout_loglevel(log_level) # Store the runtime configuration for this ops connection if not eucarc: if credpath: eucarc = Eucarc(filepath=credpath) else: eucarc = Eucarc() self.eucarc = eucarc # Set the connection params... self._try_verbose = verbose_requests self._is_secure = is_secure if aws_secret_access_key: self.eucarc.aws_secret_key = aws_secret_access_key if aws_access_key_id: self.eucarc.aws_access_key = aws_access_key_id self._service_url = service_url if not (host or port or path): if self.service_url: urlp = urlparse(self.service_url) host = host or urlp.hostname port = port or urlp.port path = path or urlp.path self.service_host = host self.service_port = port or getattr(self.eucarc, 'service_port', None) self.service_path = path self.service_region = region # Build out kwargs used to create service connection/client # Pass all the args/kwargs provided at init to the create_connection_kwargs method for the # ops class to use to build it's kwargs as needed. self._connection_kwargs = self.create_connection_kwargs(**init_kwargs) # Remaining setup... self._b2_connection = None self._b3_connection = None self.setup()
class SystemConnection(ServiceConnection): def __init__( self, hostname, username="******", password=None, keypath=None, proxy_hostname=None, proxy_username="******", proxy_password=None, proxy_keypath=None, config_yml=None, config_qa=None, credpath=None, aws_access_key=None, aws_secret_key=None, log_level="INFO", boto_debug_level=0, euca_user="******", euca_account="eucalyptus", ): self.clc_connect_kwargs = { "hostname": hostname, "username": username, "password": password, "keypath": keypath, "proxy_hostname": proxy_hostname, "proxy_username": proxy_username, "proxy_password": proxy_password, "proxy_keypath": proxy_keypath, } self._clc_machine = None self.hostname = hostname self.config_qa = config_qa self.config_yml = config_yml # self._aws_access_key = aws_access_key # self._aws_secret_key = aws_secret_key self._eucahosts = {} self._credpath = credpath self.log = Eulogger(identifier=self.__class__.__name__, stdout_level=log_level) self.creds = AutoCreds( credpath=self._credpath, aws_access_key=aws_access_key, aws_secret_key=aws_secret_key, aws_account_name=euca_account, aws_user_name=euca_user, logger=self.log, **self.clc_connect_kwargs ) super(SystemConnection, self).__init__( hostname=hostname, aws_secret_key=self.creds.aws_secret_key, aws_access_key=self.creds.aws_access_key, logger=self.log, boto_debug_level=boto_debug_level, ) def set_loglevel(self, level, parent=False): """ wrapper for log.setLevel, accept int or string. Levels can be found in logging class. At the time this was written they are: CRITICAL:50 DEBUG:10 ERROR:40 FATAL:50 INFO:20 NOTSET:0 WARN:30 WARNING:30 """ level = level or logging.NOTSET if not isinstance(level, int) and not isinstance(level, basestring): raise ValueError( 'set_loglevel. Level must be of type int or string, got: "{0}/{1}"'.format(level, type(level)) ) if isinstance(level, basestring): level = getattr(logging, str(level).upper()) return self.log.set_parentloglevel(level) @property def clc_machine(self): if not self._clc_machine: if self.clc_connect_kwargs["hostname"]: if self.eucahosts[self.clc_connect_kwargs["hostname"]]: self._clc_machine = self.eucahosts[self.clc_connect_kwargs["hostname"]] else: self._clc_machine = Machine(**self.clc_connect_kwargs) self.eucahosts[self.clc_connect_kwargs["hostname"]] = self._clc_machine return self._clc_machine @property def eucahosts(self): if not self._eucahosts: self._eucahosts = self._update_host_list() return self._eucahosts def _update_host_list(self): machines = self.get_all_machine_mappings() connect_kwargs = copy.copy(self.clc_connect_kwargs) if "hostname" in connect_kwargs: connect_kwargs.pop("hostname") hostlock = threading.Lock() def add_host(ip, services, self=self, connect_kwargs=connect_kwargs): host = EucaHost(connection=self, hostname=ip, services=services, **connect_kwargs) with hostlock: self._eucahosts[ip] = host threads = [] for ip, services in machines.iteritems(): t = threading.Thread(target=add_host, args=(ip, services)) t.start() threads.append(t) # self._eucahosts[ip] = EucaHost(connection=self, hostname=ip, services=services, # **connect_kwargs) for t in threads: t.join() return self._eucahosts def get_host_by_hostname(self, hostname): return self.eucahosts.get(hostname, None) def get_hosts_by_service_type(self, servicetype): ret_list = [] for ip, host in self.eucahosts.iteritems(): for service in host.services: if service.type == servicetype: ret_list.append(host) return ret_list def get_hosts_for_cloud_controllers(self): clc = None return self.get_hosts_by_service_type(servicetype="eucalyptus") def get_hosts_for_node_controllers(self, partition=None, instanceid=None): ncs = self.get_hosts_by_service_type(servicetype="node") if not partition and not instanceid: return ncs retlist = [] for nc in ncs: if instanceid: for instance in nc.instances: if instance == instanceid: return [nc] if nc.partition == partition: retlist.append(nc) return retlist def get_hosts_cluster_controllers(self, partition=None): ccs = self.get_hosts_by_service_type(servicetype="cluster") if not partition: return ccs retlist = [] for cc in ccs: if cc.partition == partition: retlist.append(cc) return retlist def get_hosts_for_storage_controllers(self, partition=None): scs = self.get_hosts_by_service_type(servicetype="storage") if not partition: return scs retlist = [] for sc in scs: if sc.partition == partition: retlist.append(sc) return retlist def get_hosts_for_ufs(self): ufs = None out = self.get_hosts_by_service_type(servicetype="user-api") if out: ufs = out[0] return ufs def get_hosts_for_walrus(self): walrus = None out = self.get_hosts_by_service_type(servicetype="walrusbackend") if out: walrus = out[0] return walrus def show_cloud_legacy_summary(self, repo_info=True, print_method=None, file_path=None, print_table=True): """ Creates a table representing the legacy Eutester/QA reprsentation of a Eucalyptus cloud. This can be used for legacy eutester tests, etc.. :param repo_info: bool, if True will use the work REPO in place of Zone for the 5th column :param print_method: method used to print this table, defaults to self.log.info :param print_table: bool, if False will return the table obj :param file_path: string representing a local file path to save this information to :return: table obj if print_table is False """ ret = "" print_method = print_method or self.log.info if repo_info: rz_col = "REPO" else: rz_col = "ZONE" pt = PrettyTable(["# HOST", "DISTRO", "VER", "ARCH", rz_col, "SERVICE CODES"]) pt.align = "l" pt.border = 0 for ip, host in self.eucahosts.iteritems(): split = host.summary_string.split() service_codes = " ".join(split[5:]) if repo_info: rz_col = "REPO" else: rz_col = split[4] pt.add_row([split[0], split[1], split[2], split[3], rz_col, service_codes]) ret += "{0}\n".format(host.summary_string) if file_path: with open(file_path, "w") as save_file: save_file.write(str(pt)) save_file.flush() if print_table: print_method("\n{0}\n".format(str(pt))) else: return pt @staticmethod def vm_state_markup(state): if state in ["shutting-down", "stopped", "stopping"]: return [1, 91] if state == "terminated": return [1, 97] if state == "running": return [1, 92] return [1, 93] def show_hosts( self, hosts=None, partition=None, service_type=None, serv_columns=None, update=True, print_method=None, print_table=True, save_file=None, ): print_method = print_method or self._show_method ins_id_len = 10 ins_type_len = 13 ins_dev_len = 16 ins_st_len = 15 ins_total = (ins_id_len + ins_dev_len + ins_type_len + ins_st_len) + 5 machine_hdr = (markup("MACHINE INFO"), 30) service_hdr = (markup("EUCALYPTUS SERVICES"), 90) pt = PrettyTable([machine_hdr[0], service_hdr[0]]) pt.header = False pt.align = "l" pt.hrules = 1 pt.max_width[machine_hdr[0]] = machine_hdr[1] total = [] eucahosts = {} if hosts is None: eucahosts = self.eucahosts elif isinstance(hosts, list): for host in hosts: eucahosts[host.hostname] = host elif isinstance(hosts, EucaHost): eucahosts[hosts.hostname] = hosts if not isinstance(eucahosts, dict): raise ValueError( "show_machine_mappings requires dict example: " '{"host ip":[host objs]}, got:"{0}/{1}"'.format(eucahosts, type(eucahosts)) ) # To format the tables services, print them all at once and then sort the table # rows string into the machines columns for hostip, host in eucahosts.iteritems(): for serv in host.services: if update: serv.update() total.append(serv) if serv.child_services: total.extend(serv.child_services) # Create a table showing the service states, grab the first 3 columns # for type, name, state, and zone servpt = self.show_services(total, print_table=False) # Get a subset of the show services fields... if serv_columns is None: fields = servpt._field_names[0:4] else: fields = servpt._fields_names[serv_columns] serv_lines = servpt.get_string(border=0, padding_width=2, fields=fields).splitlines() header = serv_lines[0] ansi_escape = re.compile(r"\x1b[^m]*m") # Now build the machine table... threads = [] hostlock = threading.Lock() # Method to allow host info to be gathered concurrently def add_host(hostip, host, self=self): assert isinstance(host, EucaHost) servbuf = header + "\n" mservices = [] # Get the child services (ie for UFS) for serv in host.services: mservices.append(serv) mservices.extend(serv.child_services) for serv in mservices: for line in serv_lines: # Remove the ansi markup for parsing purposes, but leave it in the # displayed line clean_line = ansi_escape.sub("", line) splitline = clean_line.split() if len(splitline) < 2: continue line_type = splitline[0] line_name = splitline[1] # Pull matching lines out of the pre-formatted service table... if ( splitline and re.match("^{0}$".format(serv.type), line_type) and re.match("^{0}$".format(serv.name), line_name) ): # Add this line to the services to be displayed for this machine if line_name not in servbuf: servbuf += line + "\n" if serv.type == "node": if getattr(serv, "instances", None): if serv.instances: vm_pt = PrettyTable( [ markup("INSTANCES", [1, 4]), markup("STATE:", [1, 4]), markup("VMTYPE:", [1, 4]), markup("ROOT_DEV:", [1, 4]), ] ) vm_pt.align = "l" vm_pt.border = 1 vm_pt.vrules = 2 vm_pt.hrules = 0 for x in serv.instances: vm_pt.add_row( [ x.id, markup(x.state, self.vm_state_markup(x.state)), x.instance_type, x.root_device_type, ] ) servbuf += "{0}\n".format(vm_pt) av_pt = host.helpers.node_controller.show_availability_for_node(print_table=False) servbuf += av_pt.get_string() ps_sum_pt = host.show_euca_process_summary(print_table=False) servbuf += "\n" + ps_sum_pt.get_string(border=1, vrules=2, hrules=0) host_info = markup("Euca Versions:").ljust(machine_hdr[1]) host_info += "Cloud: {0}".format(host.get_eucalyptus_version()).ljust(machine_hdr[1]) host_info += "2ools: {0}".format(host.get_euca2ools_version()).ljust(machine_hdr[1]) host_info += markup("Hostname:").ljust(machine_hdr[1]) host_info += str(host.hostname).ljust(machine_hdr[1]) sys_pt = host.show_sys_info(print_table=False) host_info += "{0}".format(sys_pt) with hostlock: pt.add_row( [ markup("HOST:") + markup(hostip, [1, 94]), markup("EUCALYPTUS SERVICES:") + markup("[ {0} ]".format(" ".join(str(x) for x in host.euca_service_codes)), [1, 34]), ] ) pt.add_row([host_info, servbuf]) for hostip, host in eucahosts.iteritems(): t = threading.Thread(target=add_host, args=(hostip, host)) t.start() threads.append(t) for t in threads: t.join() if save_file: with open(save_file, "w") as sf: sf.write("\n{0}\n".format(pt.get_string())) if print_table: # print_method("\n{0}\n".format(pt.get_string(sortby=pt.field_names[1]))) print_method("\n{0}\n".format(pt.get_string())) else: return pt def build_machine_dict_from_config(cls): raise NotImplementedError() def build_machine_dict_from_cloud_services(self): raise NotImplementedError("not yet implemented")
class RemoteCommands(object): """ Utility to run commands on remote machines via ssh in batches. """ def __init__(self, hostfile=None, ips=None, password=None, keypath=None, username='******', command='echo "ALIVE', timeout=5, thread_count=20, log_level='debug'): self.parser = argparse.ArgumentParser( description='Run a command on list of remote hosts', formatter_class=argparse.ArgumentDefaultsHelpFormatter) self.parser.add_argument('-f', '--hostfile', default=hostfile, help='file with list of ips and/or hostnames') self.parser.add_argument( '-i', '--ips', default=ips, help='comma or space separated list of ips and/or hostnames') self.parser.add_argument('-p', '--password', default=password, help='Ssh password used to connect to hosts') self.parser.add_argument( '-k', '--keypath', default=keypath, help='Local path to specific ssh key used to connect to hosts') self.parser.add_argument('-u', '--username', default=username, help='Ssh username used to connect to hosts') self.parser.add_argument('-c', '--command', default=command, help='file with list of ips and/or hostnames') self.parser.add_argument('-t', '--timeout', default=timeout, type=int, help='Ssh connection timeout in seconds') self.parser.add_argument( '-b', '--batch-timeout', default=0, type=int, help='Timeout for sum of all tasks to complete in seconds. ' 'This includes time to create all remote ' 'connections + execute commands') self.parser.add_argument( '--thread-count', default=thread_count, type=int, help='Number of threads used to run commands on hosts') self.parser.add_argument('-l', '--log-level', default=log_level, help='Loglevel') if ips or hostfile: args = "" else: args = None self.args = self.parser.parse_args(args=args) self.hostfile = self.args.hostfile self.password = self.args.password self.keypath = self.args.keypath self.username = self.args.username self.command = self.args.command self.timeout = self.args.timeout self.log_level = self.args.log_level self.results = {} self.maxwait = .5 self.ips = ips or self.args.ips or [] self.logger = Eulogger('RemoteCmds', stdout_level=self.log_level) if self.ips: if isinstance(self.ips, basestring): self.ips = str(self.ips).replace(',', ' ') self.ips = self.ips.split() else: self.ips = list(self.ips) if self.args.hostfile: with open(os.path.expanduser(self.args.hostfile)) as f: self.ips.extend(f.readlines()) if not self.ips: raise ValueError( 'No hosts provided. Use --hostfile or --ips to provide hosts to run ' 'command against') def do_ssh(self, q, lock, name, command): empty = False q = q or None while not empty: try: ssh = None logger = None self.logger.debug('Thread: {0}, in Q loop...'.format(name)) host = None try: host = q.get(timeout=self.maxwait) except Empty: empty = True break start = time.time() try: self.logger.debug('Connecting to new host:' + str(host)) logger = Eulogger(str(host)) ssh = SshConnection(host=host, username=self.username, password=self.password, keypath=self.keypath, debug_connect=True, timeout=self.args.timeout, verbose=True, logger=logger) logger.debug('host: {0} running command:{1} '.format( host, command)) out = ssh.cmd(str(command), listformat=True, timeout=self.args.timeout) logger.debug('Done with host: {0}'.format(host)) with lock: self.results[host] = { 'status': out.get('status'), 'output': out.get('output'), 'elapsed': int(time.time() - start) } except Exception as E: err = "{0}\n{1}".format(get_traceback(), E) with lock: self.results[host] = { 'status': -1, 'output': [err], 'elapsed': int(time.time() - start) } finally: logger.debug('Closing ssh to host: {0}'.format(host)) if ssh: ssh.connection.close() logger.debug('Closed ssh to host: {0}'.format(host)) try: if logger: logger.close() except: pass except Exception as SE: self.logger.error('{0}\nError in do_ssh:{0}'.format( get_traceback(), SE)) finally: if q is not None and not empty: q.task_done() self.logger.debug('Finished task in thread:{0}'.format(name)) self.logger.debug('{0}: Done with thread'.format(name)) def run_remote_commands( self, ips=None, command=None, ): command = command or self.command ips = ips or self.ips self.results = {} if not ips: self.logger.warning('No IPs provided to run_remote_commands!') return self.results command = command or "" iq = Queue() #if not ips: # raise ValueError('run_remote_commands: IP list was empty:"{0}"'.format(ips)) for ip in ips: ip = str(ip).strip().rstrip() iq.put(ip) tlock = Lock() threadcount = self.args.thread_count if threadcount > iq.qsize(): threadcount = iq.qsize() if not iq: return self.results = {} for i in range(threadcount): t = Thread(target=self.do_ssh, args=(iq, tlock, i, command)) t.daemon = True t.start() self.logger.debug('Threads started now waiting for join') if not self.args.batch_timeout: iq.join() else: start = time.time() while iq.unfinished_tasks and (time.time() - start < int( self.args.batch_timeout)): time.sleep(.5) if iq.unfinished_tasks: self.logger.warning( red('Possible unfinished tasks detected ' 'after elapsed:{0}. Queue:{1}'.format( time.time() - start, iq.queue))) time.sleep(.1 * len(ips)) for ip in ips: with tlock: if ip not in self.results.keys(): self.results[ip] = { 'status': -1, 'output': [ 'Timed out after {0} ' 'seconds'.format( int(self.args.batch_timeout)) ], 'elapsed': int(self.args.batch_timeout) } self.logger.debug('Done with join') time.sleep(self.maxwait + .1) return self.results def show_results(self, results=None, max_width=None, printmethod=None): results = results or self.results if not max_width: max_height, max_width = get_terminal_size() self.logger.debug( green('Got terminal width: {0}'.format(max_width))) max_width = max_width or 100 output_hdr = "OUTPUT" pt = PrettyTable(['HOST', 'RES', 'TIME', output_hdr]) host_w = 0 for host in results.keys(): if len(host) > host_w: host_w = len(host) res_w = 4 time_w = 6 pad_w = len(pt.field_names) * 2 pt.align = 'l' pt.hrules = 1 pt.padding_width = 0 max_width = max_width - (host_w + res_w + time_w + pad_w) pt.max_width[output_hdr] = max_width def sort_meth(ip): try: if re.match("^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$"): return struct.unpack("!L", inet_aton(ip))[0] except: pass return ip for host in sorted(results, key=sort_meth): result = self.results.get(host) output = "" for line in result.get('output'): line.rstrip() for x in xrange(0, len(line), max_width - 1): part = str('{output: <{length}}'.format( output=line[x:(x + max_width - 1)], length=max_width)) output += part status = result.get('status') if status == 0: color = green else: color = red pt.add_row([ blue(host), color(result.get('status', None)), color(result.get('elapsed', None)), color(output) ]) buf = "\n{0}\n".format(pt) if printmethod: printmethod(buf) else: print buf
class RemoteCommands(object): """ Utility to run commands on remote machines via ssh in batches. """ def __init__(self, hostfile=None, ips=None, password=None, username='******', command='echo "ALIVE', timeout=5, thread_count=20, log_level='debug'): self.parser = argparse.ArgumentParser( description='Run a command on list of remote hosts', formatter_class=argparse.ArgumentDefaultsHelpFormatter) self.parser.add_argument('-f', '--hostfile', default=hostfile, help='file with list of ips and/or hostnames') self.parser.add_argument('-i', '--ips', default=ips, help='comma or space separated list of ips and/or hostnames') self.parser.add_argument('-p', '--password', default=password, help='Ssh password used to connect to hosts') self.parser.add_argument('-u', '--username', default=username, help='Ssh username used to connect to hosts') self.parser.add_argument('-c', '--command', default=command, help='file with list of ips and/or hostnames') self.parser.add_argument('-t', '--timeout', default=timeout, type=int, help='Ssh connection timeout in seconds') self.parser.add_argument('--thread-count', default=thread_count, type=int, help='Number of threads used to run commands on hosts') self.parser.add_argument('-l', '--log-level', default=log_level, help='Loglevel') if ips or hostfile: args = "" else: args = None self.args = self.parser.parse_args(args=args) self.hostfile = self.args.hostfile self.password = self.args.password self.username = self.args.username self.command = self.args.command self.timeout = self.args.timeout self.log_level = self.args.log_level self.results = {} self.maxwait = .5 self.ips = ips or self.args.ips or [] self.logger = Eulogger('RemoteCmds', stdout_level=self.log_level) if self.ips: if isinstance(self.ips, basestring): self.ips = str(self.ips).replace(',', ' ') self.ips = self.ips.split() else: self.ips = list(self.ips) if self.args.hostfile: with open(os.path.expanduser(self.args.hostfile)) as f: self.ips.extend(f.readlines()) if not self.ips: raise ValueError('No hosts provided. Use --hostfile or --ips to provide hosts to run ' 'command against') def do_ssh(self, q, lock, name, command): empty = False q = q or None while not empty: try: ssh = None logger = None self.logger.debug('Thread: {0}, in Q loop...'.format(name)) host = None try: host = q.get(timeout=self.maxwait) except Empty: empty = True break start = time.time() try: self.logger.debug('Connecting to new host:' + str(host)) logger = Eulogger(str(host)) ssh = SshConnection(host=host, username=self.username, password=self.password, debug_connect=True, timeout=self.args.timeout, verbose=True, logger=logger) logger.debug('host: {0} running command:{1} '.format(host, command)) out = ssh.cmd(str(command), listformat=True) logger.debug('Done with host: {0}'.format(host)) elapsed = int(time.time() - start) with lock: self.results[host] = {'status': out.get('status'), 'output': out.get('output'), 'elapsed': elapsed} except Exception as E: elapsed = int(time.time() - start) with lock: self.results[host] = {'status': -1, 'output': [str(E)], 'elapsed': elapsed} finally: logger.debug('Closing ssh to host: {0}'.format(host)) if ssh: ssh.connection.close() logger.debug('Closed ssh to host: {0}'.format(host)) try: if logger: logger.close() except: pass except Exception as SE: self.logger.error('{0}\nError in do_ssh:{0}'.format(get_traceback(), SE)) finally: if q is not None and not empty: q.task_done() self.logger.debug('Finished task in thread:{0}'.format(name)) self.logger.debug('{0}: Done with thread'.format(name)) def run_remote_commands(self, ips=None, command=None, ): command = command or self.command ips = ips or self.ips if not ips: self.logger.warning('No IPs provided to run_remote_commands!') return self.results command = command or "" iq = Queue() #if not ips: # raise ValueError('run_remote_commands: IP list was empty:"{0}"'.format(ips)) for ip in ips: ip = str(ip).strip().rstrip() iq.put(ip) tlock = Lock() threadcount = self.args.thread_count if threadcount > iq.qsize(): threadcount = iq.qsize() if not iq: return self.results = {} for i in range(threadcount): t = Thread(target=self.do_ssh, args=(iq, tlock, i, command)) t.daemon = True t.start() self.logger.debug('Threads started now waiting for join') iq.join() self.logger.debug('Done with join') time.sleep(self.maxwait + .1) return self.results def show_results(self, results=None, max_width=None, printmethod=None): results = results or self.results if not max_width: max_height, max_width = get_terminal_size() host_w = 24 res_w = 4 time_w = 6 max_width = max_width - (host_w + res_w + time_w) - 5 output_hdr = "OUTPUT" pt = PrettyTable(['HOST', 'RES', 'TIME', output_hdr]) pt.align = 'l' pt.hrules = 1 pt.padding_width = 0 max_width = max_width - (host_w + res_w + time_w) pt.max_width[output_hdr] = max_width def sort_meth(ip): try: if re.match("^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$"): return struct.unpack("!L", inet_aton(ip))[0] except: pass return ip for host in sorted(results, key=sort_meth): result = self.results.get(host) output = "" for line in result.get('output'): line.rstrip() for x in xrange(0, len(line), max_width - 1): part = str('{output: <{length}}'.format(output=line[x:(x + max_width - 1)], length=max_width)) output += part status = result.get('status') if status == 0: color = green else: color = red pt.add_row([blue(host), color(result.get('status')), color(result.get('elapsed')), color(output)]) buf = "\n{0}\n".format(pt) if printmethod: printmethod(buf) else: print buf
def __init__(self, hostname=None, username='******', password=None, keypath=None, region=None, domain=None, proxy_hostname=None, proxy_password=None, clouduser_account='nephotest', clouduser_name='sys_admin', clouduser_credpath=None, clouduser_accesskey=None, clouduser_secretkey=None, cloudadmin_credpath=None, cloudadmin_accesskey=None, cloudadmin_secretkey=None, timeout=10, log_level='DEBUG', log_file=None, log_file_level='DEBUG', environment_file=None, https=False, validate_certs=False, cred_depot_hostname=None, cred_depot_username='******', cred_depot_password=None, boto2_api_version=None): """ :param hostname: CLC ssh hostname :param username: CLC ssh username :param password: CLC ssh password :param proxy_hostname: CLC ssh proxy hostname :param proxy_password: CLC ssh proxy password :param clouduser_account: :param clouduser_name: :param clouduser_credpath: :param clouduser_accesskey: :param clouduser_secretkey: :param cloudadmin_credpath: :param cloudadmin_accesskey: :param cloudadmin_secretkey: :param timeout: """ if isinstance(log_level, basestring): log_level = getattr(logging, log_level.upper(), logging.DEBUG) self.log = Eulogger("TESTER:{0}".format(hostname), stdout_level=log_level, logfile=log_file, logfile_level=log_file_level) if not hostname and environment_file: try: component = self.get_component_from_topology(environment_file, 'clc-1') hostname = component['clc-1'] except KeyError: component = self.get_component_from_topology(environment_file, 'clc') hostname = component['clc'][0] self.log.identifier = "TESTER:{0}".format(hostname) self.log = Eulogger("TESTER:{0}".format(hostname), stdout_level=log_level) self._region = region self._sysadmin = None self._cloudadmin = None self._test_user = None self._cred_depot = None self._default_timeout = timeout self._domain = domain self._https = https self._validate_certs = validate_certs self._cloud_admin_connection_info = {} self._test_user_connection_info = {} boto2_api_version = boto2_api_version or __DEFAULT_API_VERSION__ self._system_connection_info = {'hostname': hostname, 'username': username, 'password': password, 'keypath': keypath, 'proxy_hostname': proxy_hostname, 'proxy_password': proxy_password, 'proxy_username': None, 'config_qa': None, 'credpath': cloudadmin_credpath, 'aws_access_key': cloudadmin_accesskey, 'aws_secret_key': cloudadmin_secretkey, 'log_level': log_level, 'boto_debug_level': 0, 'euca_user': '******', 'euca_account': 'eucalyptus', 'https': https, 'domain': domain} self._cloud_admin_connection_info = {'aws_account_name': 'eucalyptus', 'aws_user_name': 'admin', 'credpath': cloudadmin_credpath, 'region': self.region, 'domain': self.domain, 'aws_access_key': cloudadmin_accesskey, 'aws_secret_key': cloudadmin_secretkey, 'service_connection': self.sysadmin, 'log_level': log_level, 'validate_certs': validate_certs, 'boto2_api_version': boto2_api_version, 'https': https} self._test_user_connection_info = {'aws_account_name': clouduser_account, 'aws_user_name': clouduser_name, 'credpath': clouduser_credpath, 'aws_access_key': clouduser_accesskey, 'aws_secret_key': clouduser_secretkey, 'region': self.region, 'domain': self.domain, 'log_level': log_level, 'validate_certs': validate_certs, 'boto2_api_version': boto2_api_version, 'https': https} self._cred_depot_connection_info = {'hostname': cred_depot_hostname, 'username': cred_depot_username, 'password': cred_depot_password or password, 'log_level': log_level} # TODO ?? self.test_resources = \ { '_instances': [], '_volumes': [] }
class EucaNetXml(object): def __init__(self, host=None, password=None, keypath=None, sshconnection=None, machine=None, eucalyptus_run_path='/var/run/eucalyptus', log_level='INFO'): if (machine and (sshconnection or host)) or sshconnection and host: warning = 'Duplicate and or possibly conflicting machine connection info provided:' \ 'host:{0}, sshconnection:{1}, machine:{2}'.format(host, sshconnection, machine) else: warning = "" if machine: sshconnection = machine.ssh if sshconnection: host = host or sshconnection.host password = password or sshconnection.password keypath = sshconnection or sshconnection.keypair if host: if not machine: machine = Machine(hostname=host, password=password, keypath=keypath, sshconnection=sshconnection) host = host or "unknown" self.log = Eulogger("{0}.{1}".format(self.__class__.__name__, host)) self.log.set_stdout_loglevel(log_level) if not host: self.log.warning( 'Connection info not provided for: {0}.init()'.format( self.__class__.__name__)) self.host = host self.password = password self.keypath = keypath self._machine = machine self._global_xml_path = None self._global_xml = None self.eucalyptus_run_path = eucalyptus_run_path or "" self.eucanetd_pid_file = path.join(self.eucalyptus_run_path, 'eucanetd.pid') self.global_xml_version = path.join(self.eucalyptus_run_path, 'global_network_info.version') @property def machine(self): if not self._machine: try: if self.host: self._machine = Machine(hostname=self.host, password=self.password, keypath=self.keypath) except Exception as E: self.log.warning( '{0}\nFailed to create machine object to host:"{1}", error:"{2}"' .format(get_traceback(), self.host, E)) return self._machine @machine.setter def machine(self, machine): if machine is None or isinstance(machine, Machine): self._machine = machine else: self.log.error( 'In correct machine type provided: "{0} {1}"'.format( machine, type(machine))) ############################################################################################## # Global XML methods ############################################################################################## @property def global_xml(self): try: if not self._global_xml: self._global_xml = GlobalXML( xml_element=self._get_global_xml_root(), eucanetd=self, log_level=self.log.stdout_level) return self._global_xml except Exception as E: self.log.error( "{0}\nFailed to create global xml element. Error:{1}".format( get_traceback(), E)) @global_xml.setter def global_xml(self, global_xml): if global_xml is not None and not isinstance(global_xml, GlobalXML): raise ValueError('Global xml must be of type:{0} or None'.format( GlobalXML.__name__)) @property def global_xml_path(self): # Since this file name may be different in different releases... if not self._global_xml_path: for fname in [ 'eucanetd_global_network_info.xml', 'global_network_info.xml' ]: fpath = path.join(self.eucalyptus_run_path, fname) if self.machine.is_file(fpath): self._global_xml_path = fpath break self._global_xml_path = None return self._global_xml_path def _get_global_xml_string(self, path=None): path = path or self.global_xml_path with self.machine.sftp.open(path) as f: out = f.read() return out def show_global_xml(self, path=None, indent=4, printmethod=None, printme=True): i_space = "" indent = indent or 0 for x in xrange(0, indent): i_space += " " indent = i_space xml_str = self._get_global_xml_string(path=path) xml = minidom.parseString(xml_str) if printme: printmethod = printmethod or self.log.info printmethod("\n{0}".format(xml.toprettyxml(indent=indent))) else: return xml.toprettyxml(indent=indent) def _get_global_xml_root(self, path=None): path = path or self.global_xml_path xml = ElementTree.fromstring(self._get_global_xml_string(path=path)) return xml
def __init__(self, filepath=None, string=None, sshconnection=None, keysdir=None, logger=None, loglevel='INFO'): """ Will populate a Eucalyptus Runtime Configuration (eucarc) obj with values from a local file, remote file, or string buffer. The parser expect values in the following format: export key=value For example: export S3_URL=http://169.254.123.123:8773/services/objectstorage The value 'http://169.254.123.123:8773/services/objectstorage' will be assigned to an of the eucarc obj using the lower case version of the key, ie: eucarc.s3 :param filepath: the local or remote filepath to the eucarc :param string: a string buffer containing the eucarc contents to be parsed :param sshconnection: an SshConnection() obj to a remote machine to read the eucarc at 'filepath' from. :param keysdir: A value to replace _KEY_DIR_STR (${EUCA_KEY_DIR}) with, by defualt this is the filepath, but when parsing from a string buffer filepath is unknown. Remote files will be prefixed with an sftp://<user>@<hostname>/ before the keys dir for later download. :param logger: logging.logger or equiv for logging output. By default a logger will be created with the class name as the identifier """ # init most common eucarc values to None... self._account_name = None self._account_id = None self._user_id = None self._user_name = None self._access_key = None self._secret_key = None self._ec2_url = None self._iam_url = None self._sts_url = None self._token_url = None self._cloudwatch_url = None self._elb_url = None self._cloudformation_url = None self._autoscaling_url = None self._simpleworkflow_url = None self.aws_credential_file = None self.aws_simpleworkflow_url = None self.ec2_access_key = None self.ec2_cert = None self.ec2_jvm_args = None self.ec2_private_key = None self.ec2_secret_key = None self.eucalyptus_cert = None self.eustore_url = 'http://emis.eucalyptus.com/' self._bootstrap_url = None self._properties_url = None self.s3_url = None # End of init default eucarc attrs if not logger: logger = Eulogger(identifier=self.__class__.__name__, stdout_level=loglevel) logger.set_stdout_loglevel(loglevel) self._log = logger self._debug = self.log.debug self._credpath = filepath if keysdir is None: keysdir = filepath self._keysdir = keysdir self._string = string self._sshconnection = sshconnection self._unparsed_lines = None if string: self._from_string() elif filepath: self._from_filepath(filepath=filepath, sshconnection=sshconnection, keysdir=filepath)
def do_ssh(self, q, lock, name, command): empty = False q = q or None while not empty: try: ssh = None logger = None self.logger.debug('Thread: {0}, in Q loop...'.format(name)) host = None try: host = q.get(timeout=self.maxwait) except Empty: empty = True break start = time.time() try: self.logger.debug('Connecting to new host:' + str(host)) logger = Eulogger(str(host)) ssh = SshConnection(host=host, username=self.username, password=self.password, debug_connect=True, timeout=self.args.timeout, verbose=True, logger=logger) logger.debug('host: {0} running command:{1} '.format(host, command)) out = ssh.cmd(str(command), listformat=True) logger.debug('Done with host: {0}'.format(host)) elapsed = int(time.time() - start) with lock: self.results[host] = {'status': out.get('status'), 'output': out.get('output'), 'elapsed': elapsed} except Exception as E: elapsed = int(time.time() - start) with lock: self.results[host] = {'status': -1, 'output': [str(E)], 'elapsed': elapsed} finally: logger.debug('Closing ssh to host: {0}'.format(host)) if ssh: ssh.connection.close() logger.debug('Closed ssh to host: {0}'.format(host)) try: if logger: logger.close() except: pass except Exception as SE: self.logger.error('{0}\nError in do_ssh:{0}'.format(get_traceback(), SE)) finally: if q is not None and not empty: q.task_done() self.logger.debug('Finished task in thread:{0}'.format(name)) self.logger.debug('{0}: Done with thread'.format(name))
def __init__(self, hostname=None, username='******', password=None, proxy_hostname=None, proxy_password=None, clouduser_account='nephotest', clouduser_name='admin', clouduser_credpath=None, clouduser_accesskey=None, clouduser_secretkey=None, cloudadmin_credpath=None, cloudadmin_accesskey=None, cloudadmin_secretkey=None, timeout=10, log_level='DEBUG', cred_depot_hostname=None, cred_depot_username='******', cred_depot_password=None): """ :param hostname: CLC ssh hostname :param username: CLC ssh username :param password: CLC ssh password :param proxy_hostname: CLC ssh proxy hostname :param proxy_password: CLC ssh proxy password :param clouduser_account: :param clouduser_name: :param clouduser_credpath: :param clouduser_accesskey: :param clouduser_secretkey: :param cloudadmin_credpath: :param cloudadmin_accesskey: :param cloudadmin_secretkey: :param timeout: """ self.log = Eulogger("TESTER:{0}".format(hostname), stdout_level=log_level) self._sysadmin = None self._cloudadmin = None self._test_user = None self._cred_depot = None self._default_timeout = timeout self._system_connection_info = {'hostname': hostname, 'username': username, 'password': password, 'proxy_hostname': proxy_hostname, 'proxy_password': proxy_password, 'proxy_username': None, 'config_qa': None, 'credpath': cloudadmin_credpath, 'aws_access_key': cloudadmin_accesskey, 'aws_secret_key': cloudadmin_secretkey, 'log_level': log_level, 'boto_debug_level': 0, 'euca_user': '******', 'euca_account': 'eucalyptus'} self._cloud_admin_connection_info = {'account_name': 'eucalyptus', 'user_name': 'admin', 'credpath': cloudadmin_credpath, 'access_key': cloudadmin_accesskey, 'secret_key': cloudadmin_secretkey, 'log_level': log_level} self._test_user_connection_info = {'aws_account_name': clouduser_account, 'aws_user_name': clouduser_name, 'credpath': clouduser_credpath, 'aws_access_key': clouduser_accesskey, 'aws_secret_key': clouduser_secretkey, 'log_level': log_level} self._cred_depot_connection_info = {'hostname': cred_depot_hostname, 'username': cred_depot_username, 'password': cred_depot_password or password, 'log_level': log_level}
class ConversionTask(TaggedEC2Object): _IMPORTINSTANCE = 'importinstance' _IMPORTVOLUME = 'importvolume' def __init__(self, connection=None): super(ConversionTask, self).__init__(connection) self.connection = connection self.conversiontaskid = None self.expirationtime = None self.state = None self.statusmessage = None self._importinstancetask = None self._importvolumetask = None self._instance = None self._snapshots = None self.tags = None self.notfound = False self.log = Eulogger('ConversionTask:{0}'.format(self.id)) def __repr__(self): volumes = ",".join([vol.id for vol in self.volumes]) return ('ConversionTask:"{0}", state:"{1}", instance:"{2}", ' 'volumes:"{3}", status:"{4}"'.format( str(self.conversiontaskid), self.state, self.instanceid, volumes, self.statusmessage)) @property def availabilityzone(self): if self.importvolumes: return self.importvolumes[0].availabilityzone return None @property def importvolumes(self): if self._importinstancetask: return self._importinstancetask.importvolumes if self._importvolumetask: return [self._importvolumetask] return [] @property def platform(self): if self._importinstancetask: return self._importinstancetask.platform return None @property def instanceid(self): if self._importinstancetask: return self._importinstancetask.instanceid return None @property def instance(self): if not self._instance and self.instanceid: try: ins = self._instance = self.connection.get_only_instances( instance_ids=[self.instanceid]) if ins: self._instance = ins[0] except Exception as E: self.log.warning( 'Failed to fetch instance:{0} for conversiontask:{1} err:'. format(self.instanceid, self.id, E)) return self._instance @property def volumes(self): ''' Volumes are updated during the task and there may not be a volume(s) associated with a task response right away, EUCA-9337 ''' ret = [] for im in self.importvolumes: if im and im.volume: ret.append(im.volume) return ret @property def snapshots(self): if not self._snapshots: self._snapshots = [] for volume in self.volumes: self._snapshots.extend( self.connection.get_all_snapshots( filters={'volume-id': volume.id})) return self._snapshots @property def image_id(self): if self.instance: return self.instance.image_id @property def tasktype(self): if self._importinstancetask: return self._IMPORTINSTANCE elif self._importvolumetask: return self._IMPORTVOLUME else: return None @property def id(self): return self.conversiontaskid @id.setter def id(self, taskid): self.conversiontaskid = taskid def cancel(self): params = {'ConversionTaskId': str(self.conversiontaskid)} task = self.connection.get_object('CancelConversionTask', params, ConversionTask, verb='POST') if task: self.update(updatedtask=task) return task def update(self, updatedtask=None): params = {} params['ConversionTaskId'] = str(self.conversiontaskid) if not updatedtask: updatedtask = self.connection.get_object('DescribeConversionTasks', params, ConversionTask, verb='POST') if updatedtask: self.__dict__.update(updatedtask.__dict__) else: print sys.stderr, 'Update. Failed to find task:"{0}"'\ .format(str(self.conversiontaskid)) self.notfound = True def startElement(self, name, attrs, connection): ename = name.replace('euca:', '') elem = super(ConversionTask, self).startElement(name, attrs, connection) if elem is not None: return elem if ename == 'importVolume': self._importvolumetask = ImportVolume(connection=connection) self.importvolumes.append(self._importvolumetask) return self._importvolumetask elif ename == 'importInstance': self._importinstancetask = ImportInstance(connection=connection) return self._importinstancetask elif ename == 'tagSet' or ename == 'resourceTagSet': self.tags = ResultSet([('item', Tag)]) return self.tags else: return None def endElement(self, name, value, connection): ename = name.lower().replace('euca:', '') if ename == 'conversiontaskid': self.conversiontaskid = value elif ename == 'expirationtime': self.expirationtime = value elif ename == 'state': self.state = value elif ename == 'statusmessage': self.statusmessage = value else: setattr(self, ename, value)
def __init__(self, filepath=None, string=None, sshconnection=None, keysdir=None, logger=None, loglevel='INFO'): """ Will populate a Eucalyptus Runtime Configuration (eucarc) obj with values from a local file, remote file, or string buffer. The parser expect values in the following format: export key=value For example: export S3_URL=http://169.254.123.123:8773/services/objectstorage The value 'http://169.254.123.123:8773/services/objectstorage' will be assigned to an of the eucarc obj using the lower case version of the key, ie: eucarc.s3 :param filepath: the local or remote filepath to the eucarc :param string: a string buffer containing the eucarc contents to be parsed :param sshconnection: an SshConnection() obj to a remote machine to read the eucarc at 'filepath' from. :param keysdir: A value to replace _KEY_DIR_STR (${EUCA_KEY_DIR}) with, by defualt this is the filepath, but when parsing from a string buffer filepath is unknown. Remote files will be prefixed with an sftp://<user>@<hostname>/ before the keys dir for later download. :param logger: logging.logger or equiv for logging output. By default a logger will be created with the class name as the identifier """ # init most common eucarc values to None... self._account_name = None self._account_id = None self._user_id = None self._user_name = None self._access_key = None self._secret_key = None self._ec2_url = None self._iam_url = None self._sts_url = None self._sqs_url = None self._token_url = None self._cloudwatch_url = None self._elb_url = None self._cloudformation_url = None self._autoscaling_url = None self._simpleworkflow_url = None self.aws_credential_file = None self.aws_simpleworkflow_url = None self.ec2_access_key = None self.ec2_cert = None self.ec2_jvm_args = None self.ec2_private_key = None self.ec2_secret_key = None self.eucalyptus_cert = None self.eustore_url = 'http://emis.eucalyptus.com/' self._bootstrap_url = None self._properties_url = None self.s3_url = None # End of init default eucarc attrs if not logger: logger = Eulogger(identifier=self.__class__.__name__, stdout_level=loglevel) logger.set_stdout_loglevel(loglevel) self._log = logger self._debug = self.log.debug self._credpath = filepath if keysdir is None: keysdir = filepath self._keysdir = keysdir self._string = string self._sshconnection = sshconnection self._unparsed_lines = None if string: self._from_string() elif filepath: self._from_filepath(filepath=filepath, sshconnection=sshconnection, keysdir=filepath)
class BaseElement(object): def __init__(self, xml_element, eucanetd=None, log_level='INFO'): if xml_element is not None and not isinstance(xml_element, Element): raise ValueError('xml_element must be of type: {0}.{1}'.format( Element.__module__, Element.__name__)) self._xml = xml_element self._eucanetd = eucanetd self._tag = None self._name = None self._log = Eulogger("{0}:{1}:{2}".format(self.__class__.__name__, self.tag or "", self.name or ""), stdout_level=log_level) self._update_from_xml(xml=self._xml) def _update(self): raise NotImplementedError( 'update not implemented for this class:{0}'.format( self.__class__.__name)) def __repr__(self): try: attrs = [self.__class__.__name__] if self.tag: attrs.append(str(self.tag)) if self.name: attrs.append(str(self.name)) return ":".join(attrs) except Exception as E: print '{0}\nFailed to create repr, err:{1}'.format( get_traceback(), E) self.log.error('{0}\nFailed to create repr, err:{1}'.format( get_traceback(), E)) def show(self, printme=True, printmethod=None): val_len = 70 key_len = 30 pt = PrettyTable(['key', 'value']) pt.align = 'l' pt.max_width['key'] = key_len pt.max_width['value'] = val_len pt.header = False pt.border = False pt.add_row(['tag', self.tag]) pt.add_row(['name', self.name]) for key, value in self.__dict__.iteritems(): if not str(key).startswith('_'): if not isinstance(value, list): pt.add_row([key, str(value)]) else: buf = "" max = (val_len) / (len(str(value[0])) + 2) count = 0 for v in value: count += 1 buf += "{0},".format(v) if not count % max: buf += "\n" else: buf += " " buf.strip(',') pt.add_row([key, buf]) if printme: printmethod = printmethod or self.log.info printmethod("\n{0}".format(pt)) else: return pt @property def xml(self): return getattr(self, '__xml', None) @xml.setter def xml(self, xml): if xml != self.xml: self._xml = xml self._update_from_xml(xml=self._xml) @property def tag(self): try: if getattr(self, '__tag', None) is None: if getattr(self, '__xml', None) is not None: self._tag = self.xml.tag return self._tag except Exception as E: print '{0}\nFailed to fetch tag, err:{1}'.format( get_traceback(), E) self.log.error('{0}\nFailed to fetch tag, err:{1}'.format( get_traceback(), E)) @tag.setter def tag(self, tag): if tag != self.tag: self._tag = tag self._log = Eulogger("{0}:{1}:{2}".format(self.__class__.__name__, self.tag or "", self.name or "")) @property def log(self): return self._log @property def name(self): try: if getattr(self, '__name', None) is None: if self.xml is not None: if 'name' in self.xml.attrib: self._name = self.xml.attrib.get('name') return self._name except Exception as E: print '{0}\nFailed to fetch name, err:{1}'.format( get_traceback(), E) self.log.error('{0}\nFailed to fetch name, err:{1}'.format( get_traceback(), E)) @name.setter def name(self, name): if name != self.name: self._name = name self._log = Eulogger("{0}:{1}:{2}".format(self.__class__.__name__, self.tag or "", self.name or "")) def _update_from_xml(self, xml): attrs = vars(self).keys() for var in attrs: if not var.startswith('_'): self.__delattr__(var) self._set_defaults() if xml is not None: self.tag = xml.tag self._parse(xml=xml) self._xml = xml def _set_defaults(self): # Method to set default values of any attributes prior to parsing xml pass def _get_class_by_tag(self, tag): if tag in tag_element_map: return tag_element_map.get(tag) else: return BaseElement def _is_element_list(self, xml): # todo replace this guess work with populated tag_element_map if len(xml) > 1: for child in xml: if len(xml.findall(child.tag)) > 1: return True else: return False elif xml.text: return False elif hasattr(self, xml.tag): # If this attribute exists, possibly set by defaults check it's type return isinstance(getattr(self, xml.tag), list) elif str(xml.tag).endswith('s'): # There's not enough info available so guess based on the name :-( return True def _parse(self, xml): self.log.debug('Beginning parsing of element with tag:{0}'.format( xml.tag)) if not isinstance(xml, Element): raise ValueError( 'parse expected Element type for xml, got:{0}, type:{1}'. format(xml, type(xml))) # Set attrs from xml attrib dict... for attr, value in xml.attrib.iteritems(): try: setattr(self, attr, value) except Exception as E: self.log.error( 'Failed to set attr:{0}, value:{1} for obj:{2}'.format( attr, value, self)) raise E if self._is_element_list(xml): new_list = ElementList() sub_child = None for sub_child in xml: tag = sub_child.tag tag_class = self._get_class_by_tag(tag) newobj = tag_class(xml_element=sub_child, log_level=self.log.stdout_level) new_list.append(newobj) return new_list else: for child in xml: if not child.tag: self._log.warning('No tag for element:"{0}"'.format( child.__dict__)) else: if not len(child): # No additional children so this is the final attribute setattr(self, child.tag, child.text) else: tag_class = self._get_class_by_tag(child.tag) newobj = tag_class( None, log_level=self.log.stdout_level)._parse(child) setattr(self, child.tag, newobj) return self
def name(self, name): if name != self.name: self._name = name self._log = Eulogger("{0}:{1}:{2}".format(self.__class__.__name__, self.tag or "", self.name or ""))
def do_ssh(self, q, lock, name, command): empty = False q = q or None while not empty: try: ssh = None logger = None self.logger.debug('Thread: {0}, in Q loop...'.format(name)) host = None try: host = q.get(timeout=self.maxwait) except Empty: empty = True break start = time.time() try: self.logger.debug('Connecting to new host:' + str(host)) logger = Eulogger(str(host)) ssh = SshConnection(host=host, username=self.username, password=self.password, keypath=self.keypath, debug_connect=True, timeout=self.args.timeout, verbose=True, logger=logger) logger.debug('host: {0} running command:{1} '.format( host, command)) out = ssh.cmd(str(command), listformat=True, timeout=self.args.timeout) logger.debug('Done with host: {0}'.format(host)) with lock: self.results[host] = { 'status': out.get('status'), 'output': out.get('output'), 'elapsed': int(time.time() - start) } except Exception as E: err = "{0}\n{1}".format(get_traceback(), E) with lock: self.results[host] = { 'status': -1, 'output': [err], 'elapsed': int(time.time() - start) } finally: logger.debug('Closing ssh to host: {0}'.format(host)) if ssh: ssh.connection.close() logger.debug('Closed ssh to host: {0}'.format(host)) try: if logger: logger.close() except: pass except Exception as SE: self.logger.error('{0}\nError in do_ssh:{0}'.format( get_traceback(), SE)) finally: if q is not None and not empty: q.task_done() self.logger.debug('Finished task in thread:{0}'.format(name)) self.logger.debug('{0}: Done with thread'.format(name))
class Euca2oolsImageUtils(object): #Define the bytes per gig gig = 1073741824 mb = 1048576 kb = 1024 def __init__(self, access_key=None, secret_key=None, account_id=None, region_domain=None, s3_url=None, ec2_url=None, bootstrap_url=None, ec2_cert_path=None, worker_hostname=None, worker_keypath=None, worker_username='******', worker_password=None, worker_machine=None, user_context=None, log_level='debug', destpath=None, time_per_gig=300, eof=True): self.access_key = access_key self.secret_key = secret_key self.account_id = account_id self.region_domain = region_domain self.bootstrap_url = bootstrap_url self.s3_url = s3_url self.ec2_url = ec2_url self.ec2_cert_path = ec2_cert_path self.log = Eulogger('Euca2oolsImageUtils', stdout_level=log_level) # Setup the work machine, this is the machine which will be used for # performing the 'work' (download, bundle, etc) self.worker_hostname = worker_hostname self.worker_keypath = worker_keypath self.worker_username = worker_username self.worker_password = worker_password self._worker_machine = None self._user_context = user_context if worker_machine: self._worker_machine = worker_machine self.time_per_gig = time_per_gig self.eof = eof if destpath is not None: self.destpath = str(destpath) else: self.destpath = "/disk1/storage" self.time_per_gig = time_per_gig def status_log(self, msg): return self.log.info( markup(msg, markups=[ ForegroundColor.WHITE, BackGroundColor.BG_GREEN, TextStyle.BOLD ])) @property def worker_machine(self): ''' Attempts to verify the worker passed is a Machine() class else assume it's a host name of the machine work should be performed on and attempt to return a Machine() created from the hostname param: worker Machine() or 'hostname' to be used to perform image utils work on. returns Machine obj ''' if not self._worker_machine: if self.worker_hostname: self.log.debug( 'Attempting to connect to machine: "{0}" for image utility work...' .format(self.worker_hostname)) self._worker_machine = Machine(hostname=self.worker_hostname, username=self.worker_username, password=self.worker_password, keypath=self.worker_keypath) return self._worker_machine @worker_machine.setter def worker_machine(self, machine): if isinstance(machine, Machine): self._worker_machine = machine else: raise ValueError( 'worker_machine must be of type Machine, got:"{0}/{1}"'.format( machine, type(machine))) def create_user_context(self, access_key, secret_key, account_id=None, region_domain=None, ec2_url=None, s3_url=None, bootstrap_url=None): if not (region_domain or s3_url or ec2_url): raise ValueError( 'Can not derive service endpoints for user. ' 'Must supply either region_domain:"{0}", or ec2_url:"{1}" ' 's3_url:"{2}"'.format(region_domain, ec2_url, s3_url)) access_key = access_key or self.access_key secret_key = secret_key or self.secret_key region_domain = region_domain or self.region_domain if (not access_key and secret_key and region_domain): raise ValueError( 'Must supply access_key, secret_key and region domain to ' 'create user context') user = UserContext(aws_access_key=access_key, aws_secret_key=secret_key, region=region_domain) if ec2_url: user.ec2_url = ec2_url if s3_url: user.s3_url = s3_url if bootstrap_url: user.bootstrap_url = bootstrap_url if account_id: user.account_id = account_id return user @property def user_context(self): if not self._user_context: try: self._user_context = self.create_user_context( access_key=self.access_key, secret_key=self.secret_key, region_domain=self.region_domain, ec2_url=self.ec2_url, s3_url=self.s3_url, bootstrap_url=self.bootstrap_url, account_id=self.account_id) except ValueError as VE: self.log.warning( 'Could not create user context, err:"{0}"'.format(VE)) return self._user_context @user_context.setter def user_context(self, user_context): if user_context is None or isinstance(user_context, UserContext): if user_context: self._user_context = user_context self.access_key = user_context.access_key self.secret_key = user_context.secret_key self.ec2_url = user_context.ec2_url self.s3_url = user_context.s3_url self.bootstrap_url = user_context.bootstrap_url self.account_id = user_context.account_id else: raise ValueError( 'Usercontext must be of type UserContext. Got:"{0}/{1}"'. format(user_context, type(user_context))) def getHttpRemoteImageSize(self, url, unit=None, maxredirect=5): return Euca2oolsImageUtils._getHttpRemoteImageSize( url, unit=unit, maxredirect=maxredirect) @staticmethod def _getHttpRemoteImageSize(url, unit=None, maxredirect=5, debug=None): ''' Get the remote file size from the http header of the url given Returns size in GB unless unit is given. ''' unit = unit or 1073741824 if debug is None: def printdebug(msg): print msg debug = printdebug def get_location(url, depth, maxdepth): if depth > maxdepth: raise ValueError( 'Max redirects limit has been reached:{0}/{1}'.format( depth, maxdepth)) conn = None try: url = url.replace('http://', '') host = url.split('/')[0] path = url.replace(host, '') res = None err = None retries = 5 for retry in xrange(0, retries): try: debug('HTTP HEAD request for: {0}, attempt:{1}/{2}'. format(url, retry, retries)) conn = httplib.HTTPConnection(host) conn.request("HEAD", path) res = conn.getresponse() break except Exception as HE: err = '{0}\nError attempting to fetch url:{1}, attempt:{2}/{3}, ' \ 'error:{4}'.format(get_traceback(), url, retry, retries, HE) debug(err) time.sleep(retry) if not res: err = err or "Error retrieving url:{0}".format(url) raise RuntimeError(err) location = res.getheader('location') if location and location != url: depth += 1 debug('Redirecting: depth:{0}, url:{1}'.format( depth, location)) return get_location(location, depth=depth, maxdepth=maxdepth) else: content_length = res.getheader('content-length') if content_length is None: raise ValueError( 'No content-length header found for url:{0}'. format(url)) fbytes = int(content_length) return fbytes except Exception as HE: debug('Failed to fetch content-length header from url:{0}'. format(url)) raise HE finally: if conn: conn.close() try: fbytes = get_location(url, depth=0, maxdepth=maxredirect) debug("content-length:" + str(fbytes)) if fbytes == 0: rfsize = 0 else: rfsize = (((fbytes / unit) + 1) or 1) debug("Remote file size: " + str(rfsize) + "g") except Exception, e: debug("Failed to get remote file size...") raise e return rfsize
def __init__(self, hostfile=None, ips=None, password=None, keypath=None, username='******', command='echo "ALIVE', timeout=5, thread_count=20, log_level='debug'): self.parser = argparse.ArgumentParser( description='Run a command on list of remote hosts', formatter_class=argparse.ArgumentDefaultsHelpFormatter) self.parser.add_argument('-f', '--hostfile', default=hostfile, help='file with list of ips and/or hostnames') self.parser.add_argument( '-i', '--ips', default=ips, help='comma or space separated list of ips and/or hostnames') self.parser.add_argument('-p', '--password', default=password, help='Ssh password used to connect to hosts') self.parser.add_argument( '-k', '--keypath', default=keypath, help='Local path to specific ssh key used to connect to hosts') self.parser.add_argument('-u', '--username', default=username, help='Ssh username used to connect to hosts') self.parser.add_argument('-c', '--command', default=command, help='file with list of ips and/or hostnames') self.parser.add_argument('-t', '--timeout', default=timeout, type=int, help='Ssh connection timeout in seconds') self.parser.add_argument( '-b', '--batch-timeout', default=0, type=int, help='Timeout for sum of all tasks to complete in seconds. ' 'This includes time to create all remote ' 'connections + execute commands') self.parser.add_argument( '--thread-count', default=thread_count, type=int, help='Number of threads used to run commands on hosts') self.parser.add_argument('-l', '--log-level', default=log_level, help='Loglevel') if ips or hostfile: args = "" else: args = None self.args = self.parser.parse_args(args=args) self.hostfile = self.args.hostfile self.password = self.args.password self.keypath = self.args.keypath self.username = self.args.username self.command = self.args.command self.timeout = self.args.timeout self.log_level = self.args.log_level self.results = {} self.maxwait = .5 self.ips = ips or self.args.ips or [] self.logger = Eulogger('RemoteCmds', stdout_level=self.log_level) if self.ips: if isinstance(self.ips, basestring): self.ips = str(self.ips).replace(',', ' ') self.ips = self.ips.split() else: self.ips = list(self.ips) if self.args.hostfile: with open(os.path.expanduser(self.args.hostfile)) as f: self.ips.extend(f.readlines()) if not self.ips: raise ValueError( 'No hosts provided. Use --hostfile or --ips to provide hosts to run ' 'command against')
class SystemConnection(ServiceConnection): def __init__(self, hostname, username='******', password=None, keypath=None, proxy_hostname=None, proxy_username=None, proxy_password=None, proxy_keypath=None, config_yml=None, config_qa=None, credpath=None, aws_access_key=None, aws_secret_key=None, log_level='INFO', boto_debug_level=0, euca_user='******', euca_account='eucalyptus', ): self.machine_connect_kwargs = { 'hostname': hostname, 'username': username, 'password': password, 'keypath': keypath, 'proxy_hostname': proxy_hostname, 'proxy_username': proxy_username or username, 'proxy_password': proxy_password or password, 'proxy_keypath': proxy_keypath or keypath, 'log_level': log_level } self._clc_machine = None self.hostname = hostname self.config_qa = config_qa self.config_yml = config_yml # self._aws_access_key = aws_access_key # self._aws_secret_key = aws_secret_key self._eucahosts = {} self._credpath = credpath self.log = Eulogger(identifier=self.__class__.__name__, stdout_level=log_level) self.creds = AutoCreds(credpath=self._credpath, aws_access_key=aws_access_key, aws_secret_key=aws_secret_key, aws_account_name=euca_account, aws_user_name=euca_user, logger=self.log, **self.machine_connect_kwargs) super(SystemConnection, self).__init__(hostname=hostname, aws_secret_key=self.creds.aws_secret_key, aws_access_key=self.creds.aws_access_key, logger=self.log, boto_debug_level=boto_debug_level) def set_loglevel(self, level, parent=False): """ wrapper for log.setLevel, accept int or string. Levels can be found in logging class. At the time this was written they are: CRITICAL:50 DEBUG:10 ERROR:40 FATAL:50 INFO:20 NOTSET:0 WARN:30 WARNING:30 """ level = level or logging.NOTSET if not isinstance(level, int) and not isinstance(level, basestring): raise ValueError('set_loglevel. Level must be of type int or string, got: "{0}/{1}"' .format(level, type(level))) if isinstance(level, basestring): level = getattr(logging, str(level).upper()) return self.log.set_parentloglevel(level) @property def clc_machine(self): if not self._clc_machine: hostname = self.machine_connect_kwargs['hostname'] if hostname: # See if a host exists matching the provided hostname if hostname in self.eucahosts: self._clc_machine = self.eucahosts[hostname] # See if this is a localhost connection elif self._get_clc_eucahost_for_localhost(): self._clc_machine = self._get_clc_eucahost_for_localhost() else: self._clc_machine = Machine(**self.machine_connect_kwargs) self.eucahosts[self.machine_connect_kwargs['hostname']] = self._clc_machine return self._clc_machine @property def eucahosts(self): if not self._eucahosts: self._eucahosts = self._update_host_list() return self._eucahosts def _get_clc_eucahost_for_localhost(self): ifaces = self._get_all_local_ip_interfaces() for iface, ip in ifaces: if ip in self.eucahosts: self.log.debug('CLC is bound to iface:{0} ip:{1}'.format(iface, ip)) return self.eucahosts[ip] return None def _get_all_local_ip_interfaces(self): max_possible = 1028 bytes = max_possible * 32 s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) names = array.array('B', '\0' * bytes) outbytes = struct.unpack('iL', fcntl.ioctl( s.fileno(), 0x8912, # SIOCGIFCONF struct.pack('iL', bytes, names.buffer_info()[0]) ))[0] namestr = names.tostring() interfaces = [] for i in range(0, outbytes, 40): name = namestr[i:i+16].split('\0', 1)[0] addr = namestr[i+20:i+24] ip = "{0}.{1}.{2}.{3}".format(ord(addr[0]), ord(addr[1]), ord(addr[2]), ord(addr[3])) interfaces.append((name, ip)) return interfaces def _update_host_list(self): machines = self.get_all_machine_mappings() connect_kwargs = copy.copy(self.machine_connect_kwargs) if 'hostname' in connect_kwargs: connect_kwargs.pop('hostname') hostlock = threading.Lock() def add_host(ip, services, self=self, connect_kwargs=connect_kwargs): host = EucaHost(connection=self, hostname=ip, services=services, **connect_kwargs) with hostlock: self._eucahosts[ip] = host threads = [] for ip, services in machines.iteritems(): t = threading.Thread(target=add_host, args=(ip, services)) t.start() threads.append(t) for t in threads: t.join() return self._eucahosts def get_host_by_hostname(self, hostname): return self.eucahosts.get(hostname, None) def get_hosts_by_service_type(self, servicetype): ret_list = [] for ip, host in self.eucahosts.iteritems(): for service in host.services: if service.type == servicetype: ret_list.append(host) return ret_list def get_hosts_for_cloud_controllers(self): clc = None return self.get_hosts_by_service_type(servicetype='eucalyptus') def get_hosts_for_node_controllers(self, partition=None, instanceid=None): if instanceid is not None and not isinstance(instanceid, basestring): raise ValueError('Instance id not of string type, got:"{0}"/"{1}"' .format(instanceid, type(instanceid))) ncs = self.get_hosts_by_service_type(servicetype='node') if not partition and not instanceid: return ncs retlist = [] if instanceid: try: reservation = self.ec2_connection.get_all_instances(instance_ids=[instanceid]) except: self.log.error('{0}\nFailed to find instance:"{1}" on system' .format(get_traceback(), instanceid)) return [] if reservation: instance = reservation[0].instances[0] node_addr = instance.tags.get('euca:node') if node_addr: for nc in ncs: if nc.hostname == node_addr: return [nc] if partition and partition in nc.partitions: retlist.append(nc) return retlist def get_hosts_for_cluster_controllers(self, partition=None): ccs = self.get_hosts_by_service_type(servicetype='cluster') if not partition: return ccs retlist = [] for cc in ccs: if partition in cc.partitions: retlist.append(cc) return retlist def get_hosts_for_storage_controllers(self, partition=None): scs = self.get_hosts_by_service_type(servicetype='storage') if not partition: return scs retlist = [] for sc in scs: if partition in sc.partitions: retlist.append(sc) return retlist def get_hosts_for_ufs(self): ufs = None return self.get_hosts_by_service_type(servicetype='user-api') def get_hosts_for_walrus(self): walrus = None return self.get_hosts_by_service_type(servicetype='walrusbackend') def show_cloud_legacy_summary(self, repo_info=True, print_method=None, file_path=None, print_table=True): """ Creates a table representing the legacy Eutester/QA reprsentation of a Eucalyptus cloud. This can be used for legacy eutester tests, etc.. :param repo_info: bool, if True will use the work REPO in place of Zone for the 5th column :param print_method: method used to print this table, defaults to self.log.info :param print_table: bool, if False will return the table obj :param file_path: string representing a local file path to save this information to :return: table obj if print_table is False """ ret = "" print_method = print_method or self.log.info if repo_info: rz_col = 'REPO' else: rz_col = 'ZONE' pt = PrettyTable(['# HOST', 'DISTRO', 'VER', 'ARCH', rz_col, 'SERVICE CODES']) pt.align = 'l' pt.border = 0 for ip, host in self.eucahosts.iteritems(): split = host.summary_string.split() service_codes = " ".join(split[5:]) if repo_info: rz_col = 'REPO' else: rz_col = split[4] pt.add_row([split[0], split[1], split[2], split[3], rz_col, service_codes]) ret += "{0}\n".format(host.summary_string) if file_path: with open(file_path, 'w') as save_file: save_file.write(str(pt)) save_file.flush() if print_table: print_method("\n{0}\n".format(str(pt))) else: return pt @staticmethod def vm_state_markup(state): if state in ['shutting-down', 'stopped', 'stopping']: return [1, 91] if state == 'terminated': return [1, 97] if state == 'running': return [1, 92] return [1, 93] def show_hosts(self, hosts=None, partition=None, service_type=None, serv_columns=None, update=True, print_method=None, print_table=True, save_file=None): print_method = print_method or self._show_method ins_id_len = 10 ins_type_len = 13 ins_dev_len = 16 ins_st_len = 15 ins_total = (ins_id_len + ins_dev_len + ins_type_len + ins_st_len) + 5 machine_hdr = (markup('MACHINE INFO'), 30) service_hdr = (markup('EUCALYPTUS SERVICES'), 90) pt = PrettyTable([machine_hdr[0], service_hdr[0]]) pt.header = False pt.align = 'l' pt.hrules = 1 pt.max_width[machine_hdr[0]] = machine_hdr[1] total = [] eucahosts = {} if hosts is None: eucahosts = self.eucahosts elif isinstance(hosts, list): for host in hosts: eucahosts[host.hostname] = host elif isinstance(hosts, EucaHost): eucahosts[hosts.hostname] = hosts if not isinstance(eucahosts, dict): raise ValueError('show_machine_mappings requires dict example: ' '{"host ip":[host objs]}, got:"{0}/{1}"' .format(eucahosts, type(eucahosts))) # To format the tables services, print them all at once and then sort the table # rows string into the machines columns try: sorted_ips = sorted(list(eucahosts), key=lambda ip: struct.unpack("!L", socket.inet_aton(ip))[0]) except Exception as SE: self.log.warning('"Failed to sort host list by IP, error:"{0}"'.format(SE)) sorted_ips = sorted(list(eucahosts)) for hostip in sorted_ips: host = eucahosts[hostip] for serv in host.services: if update: serv.update() total.append(serv) if serv.child_services: total.extend(serv.child_services) # Create a table showing the service states, grab the first 3 columns # for type, name, state, and zone servpt = self.show_services(total, print_table=False) # Get a subset of the show services fields... if serv_columns is None: fields = servpt._field_names[0:4] else: fields = servpt._fields_names[serv_columns] serv_lines = servpt.get_string(border=0, padding_width=2, fields=fields).splitlines() header = serv_lines[0] ansi_escape = re.compile(r'\x1b[^m]*m') # Now build the machine table... threads = [] hostlock = threading.Lock() # Method to allow host info to be gathered concurrently def add_host(hostip, host, self=self): assert isinstance(host, EucaHost) servbuf = header + "\n" mservices = [] # Get the child services (ie for UFS) for serv in host.services: mservices.append(serv) mservices.extend(serv.child_services) for serv in mservices: for line in serv_lines: # Remove the ansi markup for parsing purposes, but leave it in the # displayed line clean_line = ansi_escape.sub('', line) splitline = clean_line.split() if len(splitline) < 2: continue line_type = splitline[0] line_name = splitline[1] # Pull matching lines out of the pre-formatted service table... if (splitline and re.match("^{0}$".format(serv.type), line_type) and re.match("^{0}$".format(serv.name), line_name)): # Add this line to the services to be displayed for this machine if line_name not in servbuf: servbuf += line + "\n" if serv.type == 'node': if getattr(serv, 'instances', None): if serv.instances: vm_pt = PrettyTable([markup('INSTANCES', [1, 4]), markup('STATE:', [1, 4]), markup('VMTYPE:', [1, 4]), markup('ROOT_DEV:', [1, 4])]) vm_pt.align = 'l' vm_pt.border = 1 vm_pt.vrules = 2 vm_pt.hrules = 0 for x in serv.instances: vm_pt.add_row([x.id, markup(x.state, self.vm_state_markup(x.state)), x.instance_type, x.root_device_type]) servbuf += "{0}\n".format(vm_pt) av_pt = host.helpers.node_controller.show_availability_for_node( print_table=False) servbuf += av_pt.get_string() ps_sum_pt = host.show_euca_process_summary(print_table=False) servbuf += "\n" + ps_sum_pt.get_string(border=1, vrules=2, hrules=0) host_info = markup('Euca Versions:').ljust(machine_hdr[1]) host_info += "Cloud: {0}".format(host.get_eucalyptus_version()).ljust(machine_hdr[1]) host_info += "2ools: {0}".format(host.get_euca2ools_version()).ljust(machine_hdr[1]) host_info += markup("Hostname:").ljust(machine_hdr[1]) host_info += str(host.hostname).ljust(machine_hdr[1]) sys_pt = host.show_sys_info(print_table=False) host_info += "{0}".format(sys_pt) with hostlock: pt.add_row([markup("HOST:") + markup(hostip, [1, 94]), markup('EUCALYPTUS SERVICES:') + markup('[ {0} ]' .format(" ".join(str(x) for x in host.euca_service_codes)), [1, 34])]) pt.add_row([host_info, servbuf]) for hostip, host in eucahosts.iteritems(): t = threading.Thread(target=add_host, args=(hostip, host)) t.start() threads.append(t) for t in threads: t.join() if save_file: with open(save_file, 'w') as sf: sf.write("\n{0}\n".format(pt.get_string())) if print_table: # print_method("\n{0}\n".format(pt.get_string(sortby=pt.field_names[1]))) print_method("\n{0}\n".format(pt.get_string())) else: return pt def upgrade_cloud(self, network_mode=None, ccs=None, ncs=None, clcs=None, scs=None, ufs=None, ws=None, gpgcheck=False, yum_arg_list=None, dry_run=False, rerun=False): if rerun: if not hasattr(self, '_upgrade_dict'): raise ValueError('self._upgrade_dict not found, can not use "rerun"') return self.upgrade_cloud(**self._upgrade_dict) if yum_arg_list is None: yum_arg_list = [] if not isinstance(yum_arg_list, list): yum_arg_list = [yum_arg_list] if not gpgcheck: yum_arg_list.append("--nogpg") yum_args = " -y {0}".format(" ".join(yum_arg_list)) # Sort out the host machines by services... known_net_modes = ['EDGE', 'VPCMIDO', 'MANAGED'] if network_mode is None: try: cluster_name = self.get_all_cluster_names()[0] prop = self.get_property("{0}.cluster.networkmode".format(cluster_name)) network_mode = prop.value except: self.log.error('Could not retrieve network mode for cloud') raise if re.search('MANAGED', network_mode): network_mode = 'MANAGED' if network_mode not in known_net_modes: raise ValueError('Unknown network mode:{0}, known types {1}' .format(network_mode, ", ".join(known_net_modes))) # service arrays eucalyptus_cloud_hosts = [] eucanetd_hosts = [] ccs = ccs or self.get_hosts_for_cluster_controllers() ncs = ncs or self.get_hosts_for_node_controllers() clcs = clcs or self.get_hosts_for_cloud_controllers() scs = scs or self.get_hosts_for_storage_controllers() ufs = ufs or self.get_hosts_for_ufs() ws = ws or self.get_hosts_for_walrus() upgrade_dict = {'network_mode': network_mode, 'ccs': ccs, 'ncs': ncs, 'clcs': clcs, 'scs': scs, 'ufs': ufs, 'ws': ws, 'gpgcheck': gpgcheck, 'yum_arg_list': yum_arg_list} if dry_run: return upgrade_dict for host in clcs + ufs + scs + ws: if host not in eucalyptus_cloud_hosts: eucalyptus_cloud_hosts.append(host) if network_mode == "MANAGED": eucanetd_hosts = ccs elif network_mode == 'EDGE': eucanetd_hosts = ncs elif network_mode == 'VPCMIDO': eucanetd_hosts = clcs else: raise ValueError('Unsupported network mode: "{0}"'.format(network_mode) ) def stop_service(host, service, timeout=300): try: host.sys('service {0} stop'.format(service), code=0, timeout=timeout) except CommandExitCodeException as CE: if CE.status == 2: # service is already stopped pass else: raise try: # Shutdown all the Eucalyptus cloud services... self.log.info('Beginning upgrade. Shutting down all cloud services now...') for host in clcs: stop_service(host, 'eucalyptus-cloud') for host in eucalyptus_cloud_hosts: # Skip the CLCs which have already been stopped if host not in clcs: stop_service(host, 'eucalyptus-cloud') for host in ccs: stop_service(host, 'eucalyptus-cc') for host in ncs: stop_service(host, 'eucalyptus-nc') for host in eucanetd_hosts: stop_service(host, 'eucanetd') # Upgrade packages... self.log.info('Upgrading Eucalyptus packages on all hosts') for host in self.eucahosts.itervalues(): host.sys('yum upgrade eucalyptus {0}'.format(yum_args), code=0, timeout=400) self.log.info('Package upgrade complete, restarting cloud services now...') # Start all the Eucalyptus cloud services... # Do the CLCs first than the other Java/Cloud services self.log.info('Starting CLCs...') for host in clcs: host.sys('service eucalyptus-cloud start', code=0, timeout=300) self.log.info('Starting remaining Java Components...') for host in eucalyptus_cloud_hosts: # Skip the CLCs which have already been started if host not in clcs: host.sys('service eucalyptus-cloud start', code=0, timeout=300) self.log.info('Starting Cluster Controllers...') for host in ccs: host.sys('service eucalyptus-cc start', code=0, timeout=300) self.log.info('Starting Node Controllers...') for host in ncs: host.sys('service eucalyptus-nc start', code=0, timeout=300) self.log.info('Starting Eucanetd...') for host in eucanetd_hosts: host.sys('service eucanetd start', code=0, timeout=300) self.log.info('Upgrade Done') except: self.log.error('Upgrade failed. The upgrade params are found in self._upgrade_dict.' 'These can be used via the "rerun" argument to rerun this upgrade' 'using the same environment/machines') raise finally: # write to this dict for before/after comparison of the cloud after upgrade self._upgrade_dict = upgrade_dict def build_machine_dict_from_config(cls): raise NotImplementedError() def build_machine_dict_from_cloud_services(self): raise NotImplementedError('not yet implemented')
class Euca2oolsImageUtils(object): #Define the bytes per gig gig = 1073741824 mb = 1048576 kb = 1024 def __init__(self, access_key=None, secret_key=None, account_id=None, region_domain=None, s3_url=None, ec2_url=None, bootstrap_url=None, ec2_cert_path=None, worker_hostname=None, worker_keypath=None, worker_username='******', worker_password=None, worker_machine=None, user_context=None, log_level='debug', destpath=None, time_per_gig=300, eof=True): self.access_key = access_key self.secret_key = secret_key self.account_id = account_id self.region_domain = region_domain self.bootstrap_url = bootstrap_url self.s3_url = s3_url self.ec2_url = ec2_url self.ec2_cert_path = ec2_cert_path self.log = Eulogger('Euca2oolsImageUtils', stdout_level=log_level) # Setup the work machine, this is the machine which will be used for # performing the 'work' (download, bundle, etc) self.worker_hostname = worker_hostname self.worker_keypath = worker_keypath self.worker_username = worker_username self.worker_password = worker_password self._worker_machine = None self._user_context = user_context if worker_machine: self._worker_machine = worker_machine self.time_per_gig = time_per_gig self.eof = eof if destpath is not None: self.destpath = str(destpath) else: self.destpath = "/disk1/storage" self.time_per_gig = time_per_gig def status_log(self, msg): return self.log.info(markup(msg, markups=[ForegroundColor.WHITE, BackGroundColor.BG_GREEN, TextStyle.BOLD])) @property def worker_machine(self): ''' Attempts to verify the worker passed is a Machine() class else assume it's a host name of the machine work should be performed on and attempt to return a Machine() created from the hostname param: worker Machine() or 'hostname' to be used to perform image utils work on. returns Machine obj ''' if not self._worker_machine: if self.worker_hostname: self.log.debug('Attempting to connect to machine: "{0}" for image utility work...' .format(self.worker_hostname)) self._worker_machine = Machine(hostname=self.worker_hostname, username=self.worker_username, password=self.worker_password, keypath=self.worker_keypath) return self._worker_machine @worker_machine.setter def worker_machine(self, machine): if isinstance(machine, Machine): self._worker_machine = machine else: raise ValueError('worker_machine must be of type Machine, got:"{0}/{1}"' .format(machine, type(machine))) def create_user_context(self, access_key, secret_key, account_id=None, region_domain=None, ec2_url=None, s3_url=None, bootstrap_url=None): if not (region_domain or s3_url or ec2_url): raise ValueError('Can not derive service endpoints for user. ' 'Must supply either region_domain:"{0}", or ec2_url:"{1}" ' 's3_url:"{2}"'.format(region_domain, ec2_url, s3_url)) access_key = access_key or self.access_key secret_key = secret_key or self.secret_key region_domain = region_domain or self.region_domain if (not access_key and secret_key and region_domain): raise ValueError('Must supply access_key, secret_key and region domain to ' 'create user context') user = UserContext(aws_access_key=access_key, aws_secret_key=secret_key, region=region_domain) if ec2_url: user.ec2_url = ec2_url if s3_url: user.s3_url = s3_url if bootstrap_url: user.bootstrap_url = bootstrap_url if account_id: user.account_id = account_id return user @property def user_context(self): if not self._user_context: try: self._user_context = self.create_user_context(access_key=self.access_key, secret_key=self.secret_key, region_domain=self.region_domain, ec2_url=self.ec2_url, s3_url=self.s3_url, bootstrap_url=self.bootstrap_url, account_id=self.account_id) except ValueError as VE: self.log.warning('Could not create user context, err:"{0}"'.format(VE)) return self._user_context @user_context.setter def user_context(self, user_context): if user_context is None or isinstance(user_context, UserContext): if user_context: self._user_context = user_context self.access_key = user_context.access_key self.secret_key = user_context.secret_key self.ec2_url = user_context.ec2_url self.s3_url = user_context.s3_url self.bootstrap_url = user_context.bootstrap_url self.account_id = user_context.account_id else: raise ValueError('Usercontext must be of type UserContext. Got:"{0}/{1}"' .format(user_context, type(user_context))) def getHttpRemoteImageSize(self, url, unit=None, maxredirect=5): return Euca2oolsImageUtils._getHttpRemoteImageSize(url, unit=unit, maxredirect=maxredirect) @staticmethod def _getHttpRemoteImageSize(url, unit=None, maxredirect=5, debug=None): ''' Get the remote file size from the http header of the url given Returns size in GB unless unit is given. ''' unit = unit or 1073741824 if debug is None: def printdebug(msg): print msg debug = printdebug def get_location(url, depth, maxdepth): if depth > maxdepth: raise ValueError('Max redirects limit has been reached:{0}/{1}' .format(depth, maxdepth)) conn = None try: url = url.replace('http://', '') host = url.split('/')[0] path = url.replace(host, '') res = None err = None retries = 5 for retry in xrange(0, retries): try: debug('HTTP HEAD request for: {0}, attempt:{1}/{2}' .format(url, retry, retries)) conn = httplib.HTTPConnection(host) conn.request("HEAD", path) res = conn.getresponse() break except Exception as HE: err = '{0}\nError attempting to fetch url:{1}, attempt:{2}/{3}, ' \ 'error:{4}'.format(get_traceback(), url, retry, retries, HE) debug(err) time.sleep(retry) if not res: err = err or "Error retrieving url:{0}".format(url) raise RuntimeError(err) location = res.getheader('location') if location and location != url: depth += 1 debug('Redirecting: depth:{0}, url:{1}'.format(depth, location)) return get_location(location, depth=depth,maxdepth=maxdepth) else: content_length = res.getheader('content-length') if content_length is None: raise ValueError('No content-length header found for url:{0}' .format(url)) fbytes = int(content_length) return fbytes except Exception as HE: debug('Failed to fetch content-length header from url:{0}'.format(url)) raise HE finally: if conn: conn.close() try: fbytes = get_location(url, depth=0, maxdepth=maxredirect) debug("content-length:" + str(fbytes)) if fbytes == 0: rfsize = 0 else: rfsize = (((fbytes/unit) + 1) or 1) debug("Remote file size: " + str(rfsize) + "g") except Exception, e: debug("Failed to get remote file size...") raise e return rfsize
class ConversionTask(TaggedEC2Object): _IMPORTINSTANCE = 'importinstance' _IMPORTVOLUME = 'importvolume' def __init__(self, connection=None): super(ConversionTask, self).__init__(connection) self.connection = connection self.conversiontaskid = None self.expirationtime = None self.state = None self.statusmessage = None self._importinstancetask = None self._importvolumetask = None self._instance = None self._snapshots = None self.tags = None self.notfound = False self.log = Eulogger('ConversionTask:{0}'.format(self.id)) def __repr__(self): volumes = ",".join([vol.id for vol in self.volumes]) return ('ConversionTask:"{0}", state:"{1}", instance:"{2}", ' 'volumes:"{3}", status:"{4}"' .format(str(self.conversiontaskid), self.state, self.instanceid, volumes, self.statusmessage)) @property def availabilityzone(self): if self.importvolumes: return self.importvolumes[0].availabilityzone return None @property def importvolumes(self): if self._importinstancetask: return self._importinstancetask.importvolumes if self._importvolumetask: return [self._importvolumetask] return [] @property def platform(self): if self._importinstancetask: return self._importinstancetask.platform return None @property def instanceid(self): if self._importinstancetask: return self._importinstancetask.instanceid return None @property def instance(self): if not self._instance and self.instanceid: try: ins = self._instance = self.connection.get_only_instances( instance_ids=[self.instanceid]) if ins: self._instance = ins[0] except Exception as E: self.log.warning('Failed to fetch instance:{0} for conversiontask:{1} err:' .format(self.instanceid, self.id, E)) return self._instance @property def volumes(self): ''' Volumes are updated during the task and there may not be a volume(s) associated with a task response right away, EUCA-9337 ''' ret = [] for im in self.importvolumes: if im and im.volume: ret.append(im.volume) return ret @property def snapshots(self): if not self._snapshots: self._snapshots = [] for volume in self.volumes: self._snapshots.extend(self.connection.get_all_snapshots( filters={'volume-id':volume.id})) return self._snapshots @property def image_id(self): if self.instance: return self.instance.image_id @property def tasktype(self): if self._importinstancetask: return self._IMPORTINSTANCE elif self._importvolumetask: return self._IMPORTVOLUME else: return None @property def id(self): return self.conversiontaskid @id.setter def id(self, taskid): self.conversiontaskid = taskid def cancel(self): params = {'ConversionTaskId':str(self.conversiontaskid)} task = self.connection.get_object('CancelConversionTask', params, ConversionTask, verb='POST') if task: self.update(updatedtask=task) return task def update(self, updatedtask=None): params = {} params['ConversionTaskId'] = str(self.conversiontaskid) if not updatedtask: updatedtask = self.connection.get_object('DescribeConversionTasks', params, ConversionTask, verb='POST') if updatedtask: self.__dict__.update(updatedtask.__dict__) else: print sys.stderr, 'Update. Failed to find task:"{0}"'\ .format(str(self.conversiontaskid)) self.notfound = True def startElement(self, name, attrs, connection): ename = name.replace('euca:','') elem = super(ConversionTask, self).startElement(name, attrs, connection) if elem is not None: return elem if ename == 'importVolume': self._importvolumetask = ImportVolume(connection=connection) self.importvolumes.append(self._importvolumetask) return self._importvolumetask elif ename == 'importInstance': self._importinstancetask = ImportInstance(connection=connection) return self._importinstancetask elif ename == 'tagSet' or ename == 'resourceTagSet': self.tags = ResultSet([('item', Tag)]) return self.tags else: return None def endElement(self, name, value, connection): ename = name.lower().replace('euca:','') if ename == 'conversiontaskid': self.conversiontaskid = value elif ename == 'expirationtime': self.expirationtime = value elif ename == 'state': self.state = value elif ename == 'statusmessage': self.statusmessage = value else: setattr(self, ename, value)
class TestController(object): def __init__(self, hostname=None, username='******', password=None, keypath=None, region=None, domain=None, proxy_hostname=None, proxy_password=None, clouduser_account='nephotest', clouduser_name='sys_admin', clouduser_credpath=None, clouduser_accesskey=None, clouduser_secretkey=None, cloudadmin_credpath=None, cloudadmin_accesskey=None, cloudadmin_secretkey=None, timeout=10, log_level='DEBUG', log_file=None, log_file_level='DEBUG', environment_file=None, https=False, validate_certs=False, cred_depot_hostname=None, cred_depot_username='******', cred_depot_password=None, boto2_api_version=None): """ :param hostname: CLC ssh hostname :param username: CLC ssh username :param password: CLC ssh password :param proxy_hostname: CLC ssh proxy hostname :param proxy_password: CLC ssh proxy password :param clouduser_account: :param clouduser_name: :param clouduser_credpath: :param clouduser_accesskey: :param clouduser_secretkey: :param cloudadmin_credpath: :param cloudadmin_accesskey: :param cloudadmin_secretkey: :param timeout: """ if isinstance(log_level, basestring): log_level = getattr(logging, log_level.upper(), logging.DEBUG) self.log = Eulogger("TESTER:{0}".format(hostname), stdout_level=log_level, logfile=log_file, logfile_level=log_file_level) if not hostname and environment_file: try: component = self.get_component_from_topology( environment_file, 'clc-1') hostname = component['clc-1'] except KeyError: component = self.get_component_from_topology( environment_file, 'clc') hostname = component['clc'][0] self.log.identifier = "TESTER:{0}".format(hostname) self.log = Eulogger("TESTER:{0}".format(hostname), stdout_level=log_level) self._region = region self._sysadmin = None self._cloudadmin = None self._test_user = None self._cred_depot = None self._default_timeout = timeout self._domain = domain self._https = https self._validate_certs = validate_certs self._cloud_admin_connection_info = {} self._test_user_connection_info = {} boto2_api_version = boto2_api_version or __DEFAULT_API_VERSION__ self._system_connection_info = { 'hostname': hostname, 'username': username, 'password': password, 'keypath': keypath, 'proxy_hostname': proxy_hostname, 'proxy_password': proxy_password, 'proxy_username': None, 'config_qa': None, 'credpath': cloudadmin_credpath, 'aws_access_key': cloudadmin_accesskey, 'aws_secret_key': cloudadmin_secretkey, 'log_level': log_level, 'boto_debug_level': 0, 'euca_user': '******', 'euca_account': 'eucalyptus', 'https': https, 'domain': domain } self._cloud_admin_connection_info = { 'aws_account_name': 'eucalyptus', 'aws_user_name': 'admin', 'credpath': cloudadmin_credpath, 'region': self.region, 'domain': self.domain, 'aws_access_key': cloudadmin_accesskey, 'aws_secret_key': cloudadmin_secretkey, 'service_connection': self.sysadmin, 'log_level': log_level, 'validate_certs': validate_certs, 'boto2_api_version': boto2_api_version, 'https': https } self._test_user_connection_info = { 'aws_account_name': clouduser_account, 'aws_user_name': clouduser_name, 'credpath': clouduser_credpath, 'aws_access_key': clouduser_accesskey, 'aws_secret_key': clouduser_secretkey, 'region': self.region, 'domain': self.domain, 'log_level': log_level, 'validate_certs': validate_certs, 'boto2_api_version': boto2_api_version, 'https': https } self._cred_depot_connection_info = { 'hostname': cred_depot_hostname, 'username': cred_depot_username, 'password': cred_depot_password or password, 'log_level': log_level } # TODO ?? self.test_resources = \ { '_instances': [], '_volumes': [] } def __repr__(self): try: myrepr = "{0}:{1}(sysadmin+eucalyptus/admin)".format( self.__class__.__name__, self._system_connection_info.get('hostname', "")) return myrepr except Exception as E: self.log.debug(E) return str(self.__class__.__name__) @property def domain(self): if self._domain is None: prop_name = 'system.dns.dnsdomain' try: # First try from the cloud property... region_prop = self.sysadmin.get_property(prop_name) if region_prop.value: self._domain = region_prop.value except Exception as E: self.log.error( '{0}\nError fetching cloud property:"{1}". Error:"{2}"'. format(get_traceback(), prop_name, E)) return self._domain or self.region @property def region(self): return self._region @region.setter def region(self, value): if self._region is not None and self._region != value: self.log.error('Can not change region once it has been set') raise ValueError('Can not change region once it has been set') self._cloud_admin_connection_info['region'] = value self._test_user_connection_info['region'] = value self._region = value @property def cred_depot(self): if not self._cred_depot and self._cred_depot_connection_info.get( 'hostname'): try: self._cred_depot = Machine(**self._cred_depot_connection_info) except Exception as E: self.log.error( '{0}\nError connecting to cred depot machine:"{1}"'.format( get_traceback(), E)) raise E return self._cred_depot @property def sysadmin(self): if not self._sysadmin: if not self._system_connection_info.get('hostname', None): return None try: self._sysadmin = SystemConnection( **self._system_connection_info) except Exception as TE: self.log.error( '{0}\nCould not create sysadmin interface, timed out: "{1}"' .format(get_traceback(), TE)) raise TE return self._sysadmin @property def admin(self): if not self._cloudadmin: try: conn_info = self._cloud_admin_connection_info if (conn_info.get('credpath') or (conn_info.get('aws_access_key') and conn_info.get('aws_secret_key'))): if conn_info.get('credpath'): conn_info['machine'] = self.cred_depot else: rc_config = self.sysadmin.creds or {} rc_config.domain = self.domain rc_config.region = self.region conn_info['eucarc'] = rc_config self._cloudadmin = UserContext(**conn_info) except Exception as E: self.log.error( '{0}\nError creating admin user, err:"{1}"'.format( get_traceback(), E)) raise E return self._cloudadmin @property def user(self): if not self._test_user: try: self._test_user = self.create_user_using_cloudadmin( **self._test_user_connection_info) except Exception as E: self.log.error( '{0}\nError Creating test user, error:"{1}"'.format( get_traceback(), E)) raise E return self._test_user @user.setter def user(self, user): if user is None: self._test_user = None if isinstance(user, UserContext): self._test_user = user return raise ValueError('Unsupported type for test_user:"******", "{1}"'.format( user, type(user))) def reset_connections(self): self._sysadmin = None self._cloudadmin = None self._test_user = None def get_user_by_name(self, aws_account_name, aws_user_name, aws_account_id=None, machine=None, service_connection=None, region=None, domain=None, validate_certs=False, path='/', https=None, log_level=None, boto2_api_version=None): """ Fetch an existing cloud user and convert into a usercontext object. For checking basic existence of a cloud user, use the iam interface instead. """ boto2_api_version = boto2_api_version or \ self._test_user_connection_info.get('boto2_api_version', None) try: account_info = self.admin.iam.get_account( account_name=aws_account_name, account_id=aws_account_id) if account_info: aws_account_id = account_info.get('account_id') if aws_account_name and aws_account_name != account_info.get( 'account_name'): raise ValueError( 'Account id incorrect:{0} for provided account name:{1}' .format(aws_account_id, aws_account_name)) user = self.admin.iam.get_user_info( user_name=aws_user_name, delegate_account=aws_account_id) else: raise ValueError( 'Account not found using id:"{0}", name:"{1}"'.format( aws_account_id, aws_account_name)) except Exception: self.log.error( 'Error fetching "account:{0}, user:{1}" has this user been created ' 'already?'.format(aws_account_name, aws_user_name)) raise if user: return self.create_user_using_cloudadmin( aws_account_name=aws_account_name, aws_user_name=aws_user_name, aws_account_id=aws_account_id, region=region, domain=domain, validate_certs=validate_certs, machine=machine, service_connection=service_connection, path=path, https=https, log_level=log_level, boto2_api_version=boto2_api_version) else: raise ValueError( 'User info not returned for "account:{0}, user:{1}"'.format( aws_account_name, aws_user_name)) def create_user_using_cloudadmin(self, aws_account_name=None, aws_user_name='admin', aws_account_id=None, aws_access_key=None, aws_secret_key=None, credpath=None, eucarc=None, machine=None, service_connection=None, path='/', region=None, domain=None, https=None, validate_certs=False, boto2_api_version=None, log_level=None): if log_level is None: log_level = self.log.stdout_level or 'DEBUG' if region is None: region = self.region if domain is None: domain = self.domain if https is None: https = self._https boto2_api_version = boto2_api_version or \ self._test_user_connection_info.get('boto2_api_version', None) self.log.debug( 'Attempting to create user with params: account:{0}, name:{1}' 'access_key:{2}, secret_key:{3}, credpath:{4}, eucarc:{5}' ', machine:{6}, service_connection:{7}, path:{8}, region:{9},' 'loglevel:{10}, https:{11}, boto2_api_version:{12}'.format( aws_account_name, aws_user_name, aws_access_key, aws_secret_key, credpath, eucarc, machine, service_connection, path, region, log_level, https, boto2_api_version)) service_connection = service_connection or self.sysadmin if eucarc: if aws_access_key: eucarc.access_key = aws_access_key if aws_secret_key: eucarc.secret_key = aws_secret_key if aws_user_name: eucarc.user_name = aws_user_name if aws_account_name: eucarc.account_name = aws_account_name if aws_account_id: eucarc.account_id = aws_account_id return UserContext(eucarc=eucarc, region=region, domain=domain, service_connection=service_connection, log_level=log_level, https=https, boto2_api_version=boto2_api_version) if aws_access_key and aws_secret_key: return UserContext(aws_access_key=aws_access_key, aws_secret_key=aws_secret_key, aws_account_name=aws_account_name, aws_user_name=aws_user_name, region=region, domain=domain, service_connection=service_connection, log_level=log_level, boto2_api_version=boto2_api_version, https=https) if credpath: return UserContext(credpath=credpath, region=region, domain=domain, machine=machine, log_level=log_level, boto2_api_version=boto2_api_version) user = {} info = self.admin.iam.get_account(account_name=aws_account_name, account_id=aws_account_id) or {} if not info: info = self.admin.iam.create_account(account_name=aws_account_name, ignore_existing=True) aws_account_id = aws_account_id or info.get('account_id', None) try: user = self.admin.iam.get_user(user_name=aws_user_name, delegate_account=aws_account_id) except BotoServerError as BE: if int(E.status) == 404: self.log.debug('User not found, attempting to create...') if not user: user = self.admin.iam.create_user( user_name=aws_user_name, delegate_account=info.get('account_name'), path=path) if not user: raise RuntimeError( 'Failed to create and/or fetch Account:"{0}", for User:"******"'. format(aws_account_name, aws_user_name)) else: info.update(user) ak = self.admin.iam.get_aws_access_key( user_name=info.get('user_name'), delegate_account=info.get('account_name')) if not ak: ak = self.admin.iam.create_access_key( user_name=info.get('user_name'), delegate_account=info.get('account_name')) try: info['access_key_id'] = ak['access_key_id'] except KeyError: err_msg = ( 'Failed to fetch access key for USER:"******", ACCOUNT:"{1}"'. format(aws_user_name, aws_account_name)) self.log.error('{0}\n{1}'.format(get_traceback(), err_msg)) raise RuntimeError(err_msg) if self.admin.iam.get_all_signing_certs( user_name=info.get('user_name'), delegate_account=info.get('account_name')): certs = True else: certs = False user = UserContext(aws_access_key=info.get('access_key_id'), aws_secret_key=info.get('secret_access_key'), aws_account_name=info.get('account_name'), aws_user_name=info.get('user_name'), region=region, domain=domain, existing_certs=certs, machine=self.sysadmin.clc_machine, service_connection=self.sysadmin, log_level=log_level, boto2_api_version=boto2_api_version, https=https) user._user_info = self.admin.iam.get_user_info( user_name=user.user_name, delegate_account=user.account_id) return user def dump_conn_debug(self, info): """ Helper method to format and display the connection info contained in a specific dict. Example: self.dump_conn_debug(self._system_connection_info) :param info: connection dict. """ try: self.log.debug('Connection info:\n{0}'.format("\n".join( "{0}:{1}".format(x, y) for x, y in info.iteritems()))) except Exception as doh: self.log.error( '{0}\nError attempting to dump connection info:{1}'.format( get_traceback(), doh)) def set_boto_logger_level(self, level='NOTSET', format_string=None): """ Set the global boto loggers levels to 'level'. ie "DEBUG", "INFO", "CRITICAL" default is "NOTSET" :param level: string matching logging class levels, or integer representing the equiv value :param format_string: logging class formatter string """ return set_boto_logger_level(level=level, format_string=format_string) def get_component_from_topology(self, environment_file, component_type=None): """ Reads eucalyptus topology from environment file and returns value of expected component. Args: environment_file - environment file to extract Eucalyptus topology component_type - type of the component that needs to be extracted Returns: a dict with component_type as key and value from the environment file """ try: with open(environment_file) as myenv: env_dict = yaml.load(myenv) except Exception as EE: self.log.error('Failed to read env file:"{0}", err:{1}'.format( environment_file, EE)) raise EE result = env_dict['default_attributes']['eucalyptus']['topology'][ component_type] return {component_type: result}
class TestController(object): def __init__(self, hostname=None, username='******', password=None, keypath=None, region=None, domain=None, proxy_hostname=None, proxy_password=None, clouduser_account='nephotest', clouduser_name='sys_admin', clouduser_credpath=None, clouduser_accesskey=None, clouduser_secretkey=None, cloudadmin_credpath=None, cloudadmin_accesskey=None, cloudadmin_secretkey=None, timeout=10, log_level='DEBUG', log_file=None, log_file_level='DEBUG', environment_file=None, https=False, validate_certs=False, cred_depot_hostname=None, cred_depot_username='******', cred_depot_password=None, boto2_api_version=None): """ :param hostname: CLC ssh hostname :param username: CLC ssh username :param password: CLC ssh password :param proxy_hostname: CLC ssh proxy hostname :param proxy_password: CLC ssh proxy password :param clouduser_account: :param clouduser_name: :param clouduser_credpath: :param clouduser_accesskey: :param clouduser_secretkey: :param cloudadmin_credpath: :param cloudadmin_accesskey: :param cloudadmin_secretkey: :param timeout: """ if isinstance(log_level, basestring): log_level = getattr(logging, log_level.upper(), logging.DEBUG) self.log = Eulogger("TESTER:{0}".format(hostname), stdout_level=log_level, logfile=log_file, logfile_level=log_file_level) if not hostname and environment_file: try: component = self.get_component_from_topology(environment_file, 'clc-1') hostname = component['clc-1'] except KeyError: component = self.get_component_from_topology(environment_file, 'clc') hostname = component['clc'][0] self.log.identifier = "TESTER:{0}".format(hostname) self.log = Eulogger("TESTER:{0}".format(hostname), stdout_level=log_level) self._region = region self._sysadmin = None self._cloudadmin = None self._test_user = None self._cred_depot = None self._default_timeout = timeout self._domain = domain self._https = https self._validate_certs = validate_certs self._cloud_admin_connection_info = {} self._test_user_connection_info = {} boto2_api_version = boto2_api_version or __DEFAULT_API_VERSION__ self._system_connection_info = {'hostname': hostname, 'username': username, 'password': password, 'keypath': keypath, 'proxy_hostname': proxy_hostname, 'proxy_password': proxy_password, 'proxy_username': None, 'config_qa': None, 'credpath': cloudadmin_credpath, 'aws_access_key': cloudadmin_accesskey, 'aws_secret_key': cloudadmin_secretkey, 'log_level': log_level, 'boto_debug_level': 0, 'euca_user': '******', 'euca_account': 'eucalyptus', 'https': https, 'domain': domain} self._cloud_admin_connection_info = {'aws_account_name': 'eucalyptus', 'aws_user_name': 'admin', 'credpath': cloudadmin_credpath, 'region': self.region, 'domain': self.domain, 'aws_access_key': cloudadmin_accesskey, 'aws_secret_key': cloudadmin_secretkey, 'service_connection': self.sysadmin, 'log_level': log_level, 'validate_certs': validate_certs, 'boto2_api_version': boto2_api_version, 'https': https} self._test_user_connection_info = {'aws_account_name': clouduser_account, 'aws_user_name': clouduser_name, 'credpath': clouduser_credpath, 'aws_access_key': clouduser_accesskey, 'aws_secret_key': clouduser_secretkey, 'region': self.region, 'domain': self.domain, 'log_level': log_level, 'validate_certs': validate_certs, 'boto2_api_version': boto2_api_version, 'https': https} self._cred_depot_connection_info = {'hostname': cred_depot_hostname, 'username': cred_depot_username, 'password': cred_depot_password or password, 'log_level': log_level} # TODO ?? self.test_resources = \ { '_instances': [], '_volumes': [] } def __repr__(self): try: myrepr = "{0}:{1}(sysadmin+eucalyptus/admin)".format( self.__class__.__name__, self._system_connection_info.get('hostname', "")) return myrepr except Exception as E: self.log.debug(E) return str(self.__class__.__name__) @property def domain(self): if self._domain is None: prop_name = 'system.dns.dnsdomain' try: # First try from the cloud property... region_prop = self.sysadmin.get_property(prop_name) if region_prop.value: self._domain = region_prop.value except Exception as E: self.log.error('{0}\nError fetching cloud property:"{1}". Error:"{2}"' .format(get_traceback(), prop_name, E)) return self._domain or self.region @property def region(self): return self._region @region.setter def region(self, value): if self._region is not None and self._region != value: self.log.error('Can not change region once it has been set') raise ValueError('Can not change region once it has been set') self._cloud_admin_connection_info['region'] = value self._test_user_connection_info['region'] = value self._region = value @property def cred_depot(self): if not self._cred_depot and self._cred_depot_connection_info.get('hostname'): try: self._cred_depot = Machine(**self._cred_depot_connection_info) except Exception as E: self.log.error('{0}\nError connecting to cred depot machine:"{1}"' .format(get_traceback(), E)) raise E return self._cred_depot @property def sysadmin(self): if not self._sysadmin: if not self._system_connection_info.get('hostname', None): return None try: self._sysadmin = SystemConnection(**self._system_connection_info) except Exception as TE: self.log.error('{0}\nCould not create sysadmin interface, timed out: "{1}"' .format(get_traceback(), TE)) raise TE return self._sysadmin @property def admin(self): if not self._cloudadmin: try: conn_info = self._cloud_admin_connection_info if (conn_info.get('credpath') or (conn_info.get('aws_access_key') and conn_info.get('aws_secret_key'))): if conn_info.get('credpath'): conn_info['machine'] = self.cred_depot else: rc_config = self.sysadmin.creds or {} rc_config.domain = self.domain rc_config.region = self.region conn_info['eucarc'] = rc_config self._cloudadmin = UserContext(**conn_info) except Exception as E: self.log.error('{0}\nError creating admin user, err:"{1}"' .format(get_traceback(), E)) raise E return self._cloudadmin @property def user(self): if not self._test_user: try: self._test_user = self.create_user_using_cloudadmin( **self._test_user_connection_info) except Exception as E: self.log.error('{0}\nError Creating test user, error:"{1}"' .format(get_traceback(), E)) raise E return self._test_user @user.setter def user(self, user): if user is None: self._test_user = None if isinstance(user, UserContext): self._test_user = user return raise ValueError('Unsupported type for test_user:"******", "{1}"'.format(user, type(user))) def reset_connections(self): self._sysadmin = None self._cloudadmin = None self._test_user = None def get_user_by_name(self, aws_account_name, aws_user_name, machine=None, service_connection=None, region=None, domain=None, validate_certs=False, path='/', https=None, log_level=None, boto2_api_version=None): """ Fetch an existing cloud user and convert into a usercontext object. For checking basic existence of a cloud user, use the iam interface instead. """ boto2_api_version = boto2_api_version or \ self._test_user_connection_info.get('boto2_api_version', None) try: user = self.admin.iam.get_user_info(user_name=aws_user_name, delegate_account=aws_account_name) except Exception: self.log.error('Error fetching "account:{0}, user:{1}" has this user been created ' 'already?'.format(aws_account_name, aws_user_name)) raise if user: return self.create_user_using_cloudadmin(aws_account_name=aws_account_name, aws_user_name=aws_user_name, region=region, domain=domain, validate_certs=validate_certs, machine=machine, service_connection=service_connection, path=path, https=https, log_level=log_level, boto2_api_version=boto2_api_version) else: raise ValueError('User info not returned for "account:{0}, user:{1}"' .format(aws_account_name, aws_user_name)) def create_user_using_cloudadmin(self, aws_account_name=None, aws_user_name='admin', aws_access_key=None, aws_secret_key=None, credpath=None, eucarc=None, machine=None, service_connection=None, path='/', region=None, domain=None, https=None, validate_certs=False, boto2_api_version=None, log_level=None): if log_level is None: log_level = self.log.stdout_level or 'DEBUG' if region is None: region = self.region if domain is None: domain = self.domain if https is None: https = self._https boto2_api_version = boto2_api_version or \ self._test_user_connection_info.get('boto2_api_version', None) self.log.debug('Attempting to create user with params: account:{0}, name:{1}' 'access_key:{2}, secret_key:{3}, credpath:{4}, eucarc:{5}' ', machine:{6}, service_connection:{7}, path:{8}, region:{9},' 'loglevel:{10}, https:{11}, boto2_api_version:{12}' .format(aws_account_name, aws_user_name, aws_access_key, aws_secret_key, credpath, eucarc, machine, service_connection, path, region, log_level, https, boto2_api_version)) service_connection = service_connection or self.sysadmin if eucarc: if aws_access_key: eucarc.access_key = aws_access_key if aws_secret_key: eucarc.secret_key = aws_secret_key if aws_user_name: eucarc.user_name = aws_user_name if aws_account_name: eucarc.account_name = aws_account_name return UserContext(eucarc=eucarc, region=region, domain=domain, service_connection=service_connection, log_level=log_level, https=https, boto2_api_version=boto2_api_version) if aws_access_key and aws_secret_key: return UserContext(aws_access_key=aws_access_key, aws_secret_key=aws_secret_key, aws_account_name=aws_account_name, aws_user_name=aws_user_name, region=region, domain=domain, service_connection=service_connection, log_level=log_level, boto2_api_version=boto2_api_version, https=https) if credpath: return UserContext(credpath=credpath, region=region, domain=domain, machine=machine, log_level=log_level, boto2_api_version=boto2_api_version) info = self.admin.iam.create_account(account_name=aws_account_name, ignore_existing=True) if info: user = self.admin.iam.create_user(user_name=aws_user_name, delegate_account=info.get('account_name'), path=path) info.update(user) else: raise RuntimeError('Failed to create and/or fetch Account:"{0}", for User:"******"' .format(aws_account_name, aws_user_name)) ak = self.admin.iam.get_aws_access_key(user_name=info.get('user_name'), delegate_account=info.get('account_name')) if not ak: ak = self.admin.iam.create_access_key(user_name=info.get('user_name'), delegate_account=info.get('account_name')) try: info['access_key_id'] = ak['access_key_id'] except KeyError: err_msg = ('Failed to fetch access key for USER:"******", ACCOUNT:"{1}"' .format(aws_user_name, aws_account_name)) self.log.error('{0}\n{1}'.format(get_traceback(), err_msg)) raise RuntimeError(err_msg) if self.admin.iam.get_all_signing_certs(user_name=info.get('user_name'), delegate_account=info.get('account_name')): certs = True else: certs = False user = UserContext(aws_access_key=info.get('access_key_id'), aws_secret_key=info.get('secret_access_key'), aws_account_name=info.get('account_name'), aws_user_name=info.get('user_name'), region=region, domain=domain, existing_certs=certs, machine=self.sysadmin.clc_machine, service_connection=self.sysadmin, log_level=log_level, boto2_api_version=boto2_api_version, https=https) user._user_info = self.admin.iam.get_user_info(user_name=user.user_name, delegate_account=user.account_id) return user def dump_conn_debug(self, info): """ Helper method to format and display the connection info contained in a specific dict. Example: self.dump_conn_debug(self._system_connection_info) :param info: connection dict. """ try: self.log.debug( 'Connection info:\n{0}' .format("\n".join("{0}:{1}".format(x, y) for x,y in info.iteritems()))) except Exception as doh: self.log.error('{0}\nError attempting to dump connection info:{1}' .format(get_traceback(), doh)) def set_boto_logger_level(self, level='NOTSET', format_string=None): """ Set the global boto loggers levels to 'level'. ie "DEBUG", "INFO", "CRITICAL" default is "NOTSET" :param level: string matching logging class levels, or integer representing the equiv value :param format_string: logging class formatter string """ return set_boto_logger_level(level=level, format_string=format_string) def get_component_from_topology(self, environment_file, component_type=None): """ Reads eucalyptus topology from environment file and returns value of expected component. Args: environment_file - environment file to extract Eucalyptus topology component_type - type of the component that needs to be extracted Returns: a dict with component_type as key and value from the environment file """ try: with open(environment_file) as myenv: env_dict = yaml.load(myenv) except Exception as EE: self.log.error('Failed to read env file:"{0}", err:{1}'.format(environment_file, EE)) raise EE result = env_dict['default_attributes']['eucalyptus']['topology'][component_type] return {component_type: result}
def __init__(self, hostname=None, username='******', password=None, keypath=None, region=None, domain=None, proxy_hostname=None, proxy_password=None, clouduser_account='nephotest', clouduser_name='sys_admin', clouduser_credpath=None, clouduser_accesskey=None, clouduser_secretkey=None, cloudadmin_credpath=None, cloudadmin_accesskey=None, cloudadmin_secretkey=None, timeout=10, log_level='DEBUG', log_file=None, log_file_level='DEBUG', environment_file=None, https=False, validate_certs=False, cred_depot_hostname=None, cred_depot_username='******', cred_depot_password=None, boto2_api_version=None): """ :param hostname: CLC ssh hostname :param username: CLC ssh username :param password: CLC ssh password :param proxy_hostname: CLC ssh proxy hostname :param proxy_password: CLC ssh proxy password :param clouduser_account: :param clouduser_name: :param clouduser_credpath: :param clouduser_accesskey: :param clouduser_secretkey: :param cloudadmin_credpath: :param cloudadmin_accesskey: :param cloudadmin_secretkey: :param timeout: """ if isinstance(log_level, basestring): log_level = getattr(logging, log_level.upper(), logging.DEBUG) self.log = Eulogger("TESTER:{0}".format(hostname), stdout_level=log_level, logfile=log_file, logfile_level=log_file_level) if not hostname and environment_file: try: component = self.get_component_from_topology( environment_file, 'clc-1') hostname = component['clc-1'] except KeyError: component = self.get_component_from_topology( environment_file, 'clc') hostname = component['clc'][0] self.log.identifier = "TESTER:{0}".format(hostname) self.log = Eulogger("TESTER:{0}".format(hostname), stdout_level=log_level) self._region = region self._sysadmin = None self._cloudadmin = None self._test_user = None self._cred_depot = None self._default_timeout = timeout self._domain = domain self._https = https self._validate_certs = validate_certs self._cloud_admin_connection_info = {} self._test_user_connection_info = {} boto2_api_version = boto2_api_version or __DEFAULT_API_VERSION__ self._system_connection_info = { 'hostname': hostname, 'username': username, 'password': password, 'keypath': keypath, 'proxy_hostname': proxy_hostname, 'proxy_password': proxy_password, 'proxy_username': None, 'config_qa': None, 'credpath': cloudadmin_credpath, 'aws_access_key': cloudadmin_accesskey, 'aws_secret_key': cloudadmin_secretkey, 'log_level': log_level, 'boto_debug_level': 0, 'euca_user': '******', 'euca_account': 'eucalyptus', 'https': https, 'domain': domain } self._cloud_admin_connection_info = { 'aws_account_name': 'eucalyptus', 'aws_user_name': 'admin', 'credpath': cloudadmin_credpath, 'region': self.region, 'domain': self.domain, 'aws_access_key': cloudadmin_accesskey, 'aws_secret_key': cloudadmin_secretkey, 'service_connection': self.sysadmin, 'log_level': log_level, 'validate_certs': validate_certs, 'boto2_api_version': boto2_api_version, 'https': https } self._test_user_connection_info = { 'aws_account_name': clouduser_account, 'aws_user_name': clouduser_name, 'credpath': clouduser_credpath, 'aws_access_key': clouduser_accesskey, 'aws_secret_key': clouduser_secretkey, 'region': self.region, 'domain': self.domain, 'log_level': log_level, 'validate_certs': validate_certs, 'boto2_api_version': boto2_api_version, 'https': https } self._cred_depot_connection_info = { 'hostname': cred_depot_hostname, 'username': cred_depot_username, 'password': cred_depot_password or password, 'log_level': log_level } # TODO ?? self.test_resources = \ { '_instances': [], '_volumes': [] }
class TestController(object): def __init__(self, hostname=None, username='******', password=None, proxy_hostname=None, proxy_password=None, clouduser_account='nephotest', clouduser_name='admin', clouduser_credpath=None, clouduser_accesskey=None, clouduser_secretkey=None, cloudadmin_credpath=None, cloudadmin_accesskey=None, cloudadmin_secretkey=None, timeout=10, log_level='DEBUG', cred_depot_hostname=None, cred_depot_username='******', cred_depot_password=None): """ :param hostname: CLC ssh hostname :param username: CLC ssh username :param password: CLC ssh password :param proxy_hostname: CLC ssh proxy hostname :param proxy_password: CLC ssh proxy password :param clouduser_account: :param clouduser_name: :param clouduser_credpath: :param clouduser_accesskey: :param clouduser_secretkey: :param cloudadmin_credpath: :param cloudadmin_accesskey: :param cloudadmin_secretkey: :param timeout: """ self.log = Eulogger("TESTER:{0}".format(hostname), stdout_level=log_level) self._sysadmin = None self._cloudadmin = None self._test_user = None self._cred_depot = None self._default_timeout = timeout self._system_connection_info = {'hostname': hostname, 'username': username, 'password': password, 'proxy_hostname': proxy_hostname, 'proxy_password': proxy_password, 'proxy_username': None, 'config_qa': None, 'credpath': cloudadmin_credpath, 'aws_access_key': cloudadmin_accesskey, 'aws_secret_key': cloudadmin_secretkey, 'log_level': log_level, 'boto_debug_level': 0, 'euca_user': '******', 'euca_account': 'eucalyptus'} self._cloud_admin_connection_info = {'account_name': 'eucalyptus', 'user_name': 'admin', 'credpath': cloudadmin_credpath, 'access_key': cloudadmin_accesskey, 'secret_key': cloudadmin_secretkey, 'log_level': log_level} self._test_user_connection_info = {'aws_account_name': clouduser_account, 'aws_user_name': clouduser_name, 'credpath': clouduser_credpath, 'aws_access_key': clouduser_accesskey, 'aws_secret_key': clouduser_secretkey, 'log_level': log_level} self._cred_depot_connection_info = {'hostname': cred_depot_hostname, 'username': cred_depot_username, 'password': cred_depot_password or password, 'log_level': log_level} def __repr__(self): try: myrepr = "{0}:{1}({2}:{3},{4}:{5})".format( self.__class__.__name__, self._system_connection_info.get('hostname', ""), self._cloud_admin_connection_info.get('account_name', ""), self._cloud_admin_connection_info.get('user_name', ""), self._test_user_connection_info.get('aws_account_name', ""), self._test_user_connection_info.get('aws_user_name', "")) return myrepr except Exception as E: self.log.debug(E) return str(self.__class__.__name__) @property def cred_depot(self): if not self._cred_depot and self._cred_depot_connection_info.get('hostname'): self._cred_depot = Machine(**self._cred_depot_connection_info) return self._cred_depot @property def sysadmin(self): if not self._sysadmin: try: self._sysadmin = SystemConnection(**self._system_connection_info) except Exception as TE: self.log.error('{0}\nCould not create sysadmin interface, timed out: "{1}"' .format(get_traceback(), TE)) raise TE return self._sysadmin @property def admin(self): if not self._cloudadmin: conn_info = self._cloud_admin_connection_info if (conn_info.get('credpath') or (conn_info.get('aws_access_key') and conn_info.get('aws_secret_key'))): if conn_info.get('credpath'): conn_info['machine'] = self.cred_depot self._cloudadmin = UserContext(**conn_info) else: self._cloudadmin = UserContext(eucarc=self.sysadmin.creds) return self._cloudadmin @property def user(self): if not self._test_user: self._test_user = self.create_user_using_cloudadmin(**self._test_user_connection_info) return self._test_user @user.setter def user(self, user): if user is None: self._test_user = None if isinstance(user, UserContext): self._test_user = user return raise ValueError('Unsupported type for test_user:"******", "{1}"'.format(user, type(user))) def reset_connections(self): self._sysadmin = None self._cloudadmin = None self._test_user = None def get_user_by_name(self, aws_account_name, aws_user_name, machine=None, service_connection=None, path='/', log_level=None): """ Fetch an existing cloud user and convert into a usercontext object. For checking basic existence of a cloud user, use the iam interface instead. """ try: user = self.admin.iam.get_user_info(user_name=aws_user_name, delegate_account=aws_account_name) except Exception: self.log.error('Error fetching "account:{0}, user:{1}" has this user been created ' 'already?'.format(aws_account_name, aws_user_name)) raise if user: return self.create_user_using_cloudadmin(aws_account_name=aws_account_name, aws_user_name=aws_user_name, machine=machine, service_connection=service_connection, path=path, log_level=log_level) else: raise ValueError('User info not returned for "account:{0}, user:{1}"' .format(aws_account_name, aws_user_name)) def create_user_using_cloudadmin(self, aws_account_name=None, aws_user_name='admin', aws_access_key=None, aws_secret_key=None, credpath=None, eucarc=None, machine=None, service_connection=None, path='/', log_level=None): if log_level is None: log_level = self.log.stdout_level or 'DEBUG' self.log.debug('Attempting to create user with params: account:{0}, name:{1}' 'access_key:{2}, secret_key:{3}, credpath:{4}, eucarc:{5}' ', machine:{6}, service_connection:{7}, path:{8}, loglevel:{9}' .format(aws_account_name, aws_user_name, aws_access_key, aws_secret_key, credpath, eucarc, machine, service_connection, path, log_level)) service_connection = service_connection or self.sysadmin if eucarc: if aws_access_key: eucarc.access_key = aws_access_key if aws_secret_key: eucarc.secret_key = aws_secret_key if aws_user_name: eucarc.user_name = aws_user_name if aws_account_name: eucarc.account_name = aws_account_name return UserContext(eucarc=eucarc, service_connection=service_connection, log_level=log_level) if aws_access_key and aws_secret_key: return UserContext(aws_access_key=aws_access_key, aws_secret_key=aws_secret_key, aws_account_name=aws_account_name, aws_user_name=aws_user_name, service_connection=service_connection, log_level=log_level) if credpath: return UserContext(credpath=credpath, machine=machine, log_level=log_level) info = self.admin.iam.create_account(account_name=aws_account_name, ignore_existing=True) if info: user = self.admin.iam.create_user(user_name=aws_user_name, delegate_account=info.get('account_name'), path=path) info.update(user) else: raise RuntimeError('Failed to create and/or fetch Account:"{0}", for User:"******"' .format(aws_account_name, aws_user_name)) ak = self.admin.iam.get_aws_access_key(user_name=info.get('user_name'), delegate_account=info.get('account_name')) if not ak: ak = self.admin.iam.create_access_key(user_name=info.get('user_name'), delegate_account=info.get('account_name')) try: info['access_key_id'] = ak['access_key_id'] except KeyError: err_msg = ('Failed to fetch access key for USER:"******", ACCOUNT:"{1}"' .format(aws_user_name, aws_account_name)) self.log.error('{0}\n{1}'.format(get_traceback(), err_msg)) raise RuntimeError(err_msg) if self.admin.iam.get_all_signing_certs(user_name=info.get('user_name'), delegate_account=info.get('account_name')): certs = True else: certs = False user = UserContext(aws_access_key=info.get('access_key_id'), aws_secret_key=info.get('secret_access_key'), aws_account_name=info.get('account_name'), aws_user_name=info.get('user_name'), existing_certs=certs, machine=self.sysadmin.clc_machine, service_connection=self.sysadmin, log_level=log_level) user._user_info = self.admin.iam.get_user_info(user_name=user.user_name, delegate_account=user.account_id) return user def wait_for_result(self, *args, **kwargs): return wait_for_result(*args, **kwargs)