def create_session(docker_image=None, docker_rm=None, echo=False, loglevel='WARNING', nocolor=False, session_type='bash', vagrant_session_name=None, vagrant_image='ubuntu/xenial64', vagrant_gui=False, vagrant_memory='1024', vagrant_num_machines='1', vagrant_provider='virtualbox', vagrant_root_folder=None, vagrant_swapsize='2G', vagrant_version='1.8.6', vagrant_virt_method='virtualbox', video=-1, walkthrough=False): """Creates a distinct ShutIt session. Sessions can be of type: bash - a bash shell is spawned and vagrant - a Vagrantfile is created and 'vagrant up'ped """ assert session_type in ('bash','docker','vagrant'), shutit_util.print_debug() shutit_global_object = shutit_global.shutit_global_object if video != -1 and video > 0: walkthrough = True if session_type in ('bash','docker'): return shutit_global_object.create_session(session_type, docker_image=docker_image, rm=docker_rm, echo=echo, walkthrough=walkthrough, walkthrough_wait=video, nocolor=nocolor, loglevel=loglevel) elif session_type == 'vagrant': if vagrant_session_name is None: vagrant_session_name = 'shutit' + shutit_util.random_id() if isinstance(vagrant_num_machines, int): vagrant_num_machines = str(vagrant_num_machines) assert isinstance(vagrant_num_machines, str) assert isinstance(int(vagrant_num_machines), int) if vagrant_root_folder is None: vagrant_root_folder = shutit_global.shutit_global_object.owd return create_session_vagrant(vagrant_session_name, vagrant_num_machines, vagrant_image, vagrant_provider, vagrant_gui, vagrant_memory, vagrant_swapsize, echo, walkthrough, nocolor, video, vagrant_version, vagrant_virt_method, vagrant_root_folder, loglevel)
def setup_prompt(self, prompt_name, prefix='default', delaybeforesend=0, loglevel=logging.DEBUG): """Use this when you've opened a new shell to set the PS1 to something sane. By default, it sets up the default expect so you don't have to worry about it and can just call shutit.send('a command'). If you want simple login and logout, please use login() and logout() within this module. Typically it would be used in this boilerplate pattern:: shutit.send('su - auser', expect=shutit_global.shutit.cfg['expect_prompts']['base_prompt'], check_exit=False) shutit.setup_prompt('tmp_prompt') shutit.send('some command') [...] shutit.set_default_shutit_pexpect_session_expect() shutit.send('exit') This function is assumed to be called whenever there is a change of environment. @param prompt_name: Reference name for prompt. @param prefix: Prompt prefix. Default: 'default' @param shutit_pexpect_child: See send() to the new prompt. Default: True @type prompt_name: string @type prefix: string """ local_prompt = prefix + '#' + shutit_util.random_id() + '> ' cfg = shutit_global.shutit.cfg cfg['expect_prompts'][prompt_name] = local_prompt # Set up the PS1 value. # Unset the PROMPT_COMMAND as this can cause nasty surprises in the output. # Set the cols value, as unpleasant escapes are put in the output if the # input is > n chars wide. # The newline in the expect list is a hack. On my work laptop this line hangs # and times out very frequently. This workaround seems to work, but I # haven't figured out why yet - imiell. shutit_global.shutit.send((" export SHUTIT_BACKUP_PS1_%s=$PS1 && PS1='%s' && unset PROMPT_COMMAND && stty sane && stty cols " + str(cfg['build']['stty_cols'])) % (prompt_name, local_prompt), expect=['\r\n' + cfg['expect_prompts'][prompt_name]], fail_on_empty_before=False, timeout=5, shutit_pexpect_child=self.pexpect_child, echo=False, loglevel=loglevel, delaybeforesend=delaybeforesend) shutit_global.shutit.log('Resetting default expect to: ' + cfg['expect_prompts'][prompt_name],level=logging.DEBUG) self.default_expect = cfg['expect_prompts'][prompt_name] # Ensure environment is set up OK. self.setup_environment(prefix)
def __init__(self, sendspec): # Stub this with a simple command for now self.sendspec = sendspec self.block_other_commands = sendspec.block_other_commands self.retry = sendspec.retry self.tries = 0 self.pid = None self.return_value = None self.start_time = None self.run_state = 'N' # State as per ps man page, but 'C' == Complete, 'N' == not started, 'F' == failed, 'S' == sleeping/running, 'T' == timed out by ShutIt self.cwd = self.sendspec.shutit_pexpect_child.send_and_get_output( ' command pwd', ignore_background=True) self.id = shutit_util.random_id() self.output_file = '/tmp/shutit_background_' + self.id + '_output.log' self.exit_code_file = '/tmp/shutit_background_' + self.id + '_exit_code_file.log' if self.sendspec.run_in_background: self.sendspec.send = ' set +m && { : $(command cd ' + self.cwd + '>' + self.output_file + ' && ' + self.sendspec.send + ' >>' + self.output_file + ' 2>&1; echo $? >' + self.exit_code_file + ') & } 2>/dev/null'
def __init__(self, sendspec): # Stub this with a simple command for now self.sendspec = sendspec self.block_other_commands = sendspec.block_other_commands self.retry = sendspec.retry self.tries = 0 self.pid = None self.return_value = None self.start_time = None self.run_state = 'N' # State as per ps man page, but 'C' == Complete, 'N' == not started, 'F' == failed, 'S' == sleeping/running, 'T' == timed out by ShutIt self.cwd = self.sendspec.shutit_pexpect_child.send_and_get_output(' command pwd', ignore_background=True) self.id = shutit_util.random_id() self.output_file = '/tmp/shutit_background_' + self.id + '_output.log' self.exit_code_file = '/tmp/shutit_background_' + self.id + '_exit_code_file.log' self.command_file = '/tmp/shutit_background_' + self.id + '_command.log' if self.sendspec.run_in_background: # TODO: consider separating out into a simple send for the part that creates the command file, the cd and the output file. Perhaps send file first and run that in the background? self.sendspec.send = ' set +m && { : $(echo "' + self.sendspec.original_send + '" >' + self.command_file + ' && command cd "' + self.cwd + '">' + self.output_file + ' && ' + self.sendspec.send + ' >>' + self.output_file + ' 2>&1; echo $? >' + self.exit_code_file + ') & } 2>/dev/null'
def create_command_file(self, expect, send): """Internal function. Do not use. Takes a long command, and puts it in an executable file ready to run. Returns the filename. """ cfg = shutit_global.shutit.cfg random_id = shutit_util.random_id() fname = cfg['build']['shutit_state_dir_base'] + '/tmp_' + random_id working_str = send self.sendline(' truncate --size 0 '+ fname) self.pexpect_child.expect(expect) size = cfg['build']['stty_cols'] - 25 while len(working_str) > 0: curr_str = working_str[:size] working_str = working_str[size:] self.sendline(' ' + shutit_util.get_command('head') + ''' -c -1 >> ''' + fname + """ << 'END_""" + random_id + """'\n""" + curr_str + """\nEND_""" + random_id) self.expect(expect) self.sendline(' chmod +x ' + fname) self.expect(expect) return fname
def __init__(self, sendspec, shutit_obj): # Stub this with a simple command for now self.sendspec = sendspec self.block_other_commands = sendspec.block_other_commands self.retry = sendspec.retry self.tries = 0 self.pid = None self.return_value = None self.start_time = None self.run_state = 'N' # State as per ps man page, but 'C' == Complete, 'N' == not started, 'F' == failed, 'S' == sleeping/running, 'T' == timed out by ShutIt self.cwd = self.sendspec.shutit_pexpect_child.send_and_get_output(' command pwd', ignore_background=True) self.id = shutit_util.random_id() self.output_file = '/tmp/shutit_background_' + self.id + '_output.log' self.exit_code_file = '/tmp/shutit_background_' + self.id + '_exit_code_file.log' self.command_file = '/tmp/shutit_background_' + self.id + '_command.log' if self.sendspec.run_in_background: # TODO: consider separating out into a simple send for the part that creates the command file, the cd and the output file. Perhaps send file first and run that in the background? self.sendspec.send = ' set +m && { : $(echo "' + self.sendspec.original_send + '" >' + self.command_file + ' && command cd "' + self.cwd + '">' + self.output_file + ' && ' + self.sendspec.send + ' >>' + self.output_file + ' 2>&1; echo $? >' + self.exit_code_file + ') & } 2>/dev/null' self.shutit_obj = shutit_obj
def __init__(self, prefix): """Represents a new 'environment' in ShutIt, which corresponds to a host or any machine-like location (eg docker container, ssh'd to host, or even a chroot jail with a /tmp folder that has not been touched by shutit. """ if prefix == 'ORIGIN_ENV': self.environment_id = prefix else: self.environment_id = shutit_util.random_id() self.module_root_dir = '/' self.modules_installed = [] # has been installed in this build self.modules_not_installed = [] # modules _known_ not to be installed self.modules_ready = [ ] # has been checked for readiness and is ready (in this build) self.modules_recorded = [] self.modules_recorded_cache_valid = False self.install_type = '' self.distro = '' self.distro_version = '' self.users = dict() self.build = {} self.build['apt_update_done'] = False self.build['emerge_update_done'] = False self.build['apk_update_done'] = False
def __init__(self, prefix): """Represents a new 'environment' in ShutIt, which corresponds to a host or any machine-like location (eg docker container, ssh'd to host, or even a chroot jail with a /tmp folder that has not been touched by shutit. """ if prefix == 'ORIGIN_ENV': self.environment_id = prefix else: self.environment_id = shutit_util.random_id() self.module_root_dir = '/' self.modules_installed = [] # has been installed in this build self.modules_not_installed = [] # modules _known_ not to be installed self.modules_ready = [] # has been checked for readiness and is ready (in this build) self.modules_recorded = [] self.modules_recorded_cache_valid = False self.install_type = '' self.distro = '' self.distro_version = '' self.users = dict() self.build = {} self.build['apt_update_done'] = False self.build['emerge_update_done'] = False self.build['apk_update_done'] = False
def setup_environment(self, prefix, delaybeforesend=0, loglevel=logging.DEBUG): """If we are in a new environment then set up a new data structure. A new environment is a new machine environment, whether that's over ssh, docker, whatever. If we are not in a new environment ensure the env_id is correct. Returns the environment id every time. """ # Set this to be the default session. shutit_global.shutit.set_default_shutit_pexpect_session(self) cfg = shutit_global.shutit.cfg environment_id_dir = cfg['build']['shutit_state_dir'] + '/environment_id' if shutit_global.shutit.file_exists(environment_id_dir,directory=True): files = shutit_global.shutit.ls(environment_id_dir) if len(files) != 1 or type(files) != list: if len(files) == 2 and (files[0] == 'ORIGIN_ENV' or files[1] == 'ORIGIN_ENV'): for f in files: if f != 'ORIGIN_ENV': environment_id = f cfg['build']['current_environment_id'] = environment_id # Workaround for CygWin terminal issues. If the envid isn't in the cfg item # Then crudely assume it is. This will drop through and then assume we are in the origin env. try: _=cfg['environment'][cfg['build']['current_environment_id']] except Exception: cfg['build']['current_environment_id'] = 'ORIGIN_ENV' break else: # See comment above re: cygwin. if shutit_global.shutit.file_exists('/cygdrive'): cfg['build']['current_environment_id'] = 'ORIGIN_ENV' else: shutit_global.shutit.fail('Wrong number of files in environment_id_dir: ' + environment_id_dir) else: if shutit_global.shutit.file_exists('/cygdrive'): environment_id = 'ORIGIN_ENV' else: environment_id = files[0] if cfg['build']['current_environment_id'] != environment_id: # Clean out any trace of this new environment, and return the already-existing one. shutit_global.shutit.send(' rm -rf ' + environment_id_dir + '/environment_id/' + environment_id, echo=False, loglevel=loglevel, delaybeforesend=delaybeforesend) return cfg['build']['current_environment_id'] if not environment_id == 'ORIGIN_ENV': return environment_id # Origin environment is a special case. if prefix == 'ORIGIN_ENV': environment_id = prefix else: environment_id = shutit_util.random_id() cfg['build']['current_environment_id'] = environment_id cfg['environment'][environment_id] = {} # Directory to revert to when delivering in bash and reversion to directory required. cfg['environment'][environment_id]['module_root_dir'] = '/' cfg['environment'][environment_id]['modules_installed'] = [] # has been installed (in this build) cfg['environment'][environment_id]['modules_not_installed'] = [] # modules _known_ not to be installed cfg['environment'][environment_id]['modules_ready'] = [] # has been checked for readiness and is ready (in this build) # Installed file info cfg['environment'][environment_id]['modules_recorded'] = [] cfg['environment'][environment_id]['modules_recorded_cache_valid'] = False cfg['environment'][environment_id]['setup'] = False # Exempt the ORIGIN_ENV from getting distro info if prefix != 'ORIGIN_ENV': shutit_global.shutit.get_distro_info(environment_id) fname = environment_id_dir + '/' + environment_id shutit_global.shutit.send(' mkdir -p ' + environment_id_dir + ' && chmod -R 777 ' + cfg['build']['shutit_state_dir_base'] + ' && touch ' + fname, echo=False, loglevel=loglevel, delaybeforesend=delaybeforesend) cfg['environment'][environment_id]['setup'] = True return environment_id
def login(self, user='******', command='su -', password=None, prompt_prefix=None, expect=None, timeout=180, escape=False, note=None, go_home=True, delaybeforesend=0.05, loglevel=logging.DEBUG): """Logs the user in with the passed-in password and command. Tracks the login. If used, used logout to log out again. Assumes you are root when logging in, so no password required. If not, override the default command for multi-level logins. If passwords are required, see setup_prompt() and revert_prompt() @param user: User to login with. Default: root @param command: Command to login with. Default: "su -" @param escape: See send(). We default to true here in case matches an expect we add. @param password: Password. @param prompt_prefix: Prefix to use in prompt setup. @param expect: See send() @param timeout: How long to wait for a response. Default: 20. @param note: See send() @param go_home: Whether to automatically cd to home. @type user: string @type command: string @type password: string @type prompt_prefix: string @type timeout: integer """ # We don't get the default expect here, as it's either passed in, or a base default regexp. r_id = shutit_util.random_id() if prompt_prefix == None: prompt_prefix = r_id cfg = shutit_global.shutit.cfg # Be helpful. if ' ' in user: shutit_global.shutit.fail('user has space in it - did you mean: login(command="' + user + '")?') if cfg['build']['delivery'] == 'bash' and command == 'su -': # We want to retain the current working directory command = 'su' if command == 'su -' or command == 'su' or command == 'login': send = command + ' ' + user else: send = command if expect == None: login_expect = cfg['expect_prompts']['base_prompt'] else: login_expect = expect # We don't fail on empty before as many login programs mess with the output. # In this special case of login we expect either the prompt, or 'user@' as this has been seen to work. general_expect = [login_expect] # Add in a match if we see user+ and then the login matches. Be careful not to match against '[email protected]:'******'@.*'+'[@#$]'] # If not an ssh login, then we can match against user + @sign because it won't clash with 'user@adasdas password:'******'ssh') == 0: general_expect = general_expect + [user+'@'] general_expect = general_expect + ['.*[@#$]'] if user == 'bash' and command == 'su -': shutit_global.shutit.log('WARNING! user is bash - if you see problems below, did you mean: login(command="' + user + '")?',level=loglevel.WARNING) shutit_global.shutit._handle_note(note,command=command + ', as user: "******"',training_input=send) # r'[^t] login:'******'last login:'******'ontinue connecting':'yes','assword':password,r'[^t] login:'******'cd',shutit_pexpect_child=self.pexpect_child,check_exit=False, echo=False, loglevel=loglevel, delaybeforesend=delaybeforesend) self.login_stack_append(r_id) shutit_global.shutit._handle_note_after(note=note)
def create_session(docker_image=None, docker_rm=None, echo=False, loglevel='WARNING', nocolor=False, session_type='bash', vagrant_session_name=None, vagrant_image='ubuntu/xenial64', vagrant_gui=False, vagrant_memory='1024', vagrant_num_machines='1', vagrant_provider='virtualbox', vagrant_root_folder=None, vagrant_swapsize='2G', vagrant_version='1.8.6', vagrant_virt_method='virtualbox', vagrant_cpu='1', video=-1, walkthrough=False): """Creates a distinct ShutIt session. Sessions can be of type: bash - a bash shell is spawned and vagrant - a Vagrantfile is created and 'vagrant up'ped """ assert session_type in ('bash','docker','vagrant'), shutit_util.print_debug() shutit_global_object = shutit_global.shutit_global_object if video != -1 and video > 0: walkthrough = True if session_type in ('bash','docker'): return shutit_global_object.create_session(session_type, docker_image=docker_image, rm=docker_rm, echo=echo, walkthrough=walkthrough, walkthrough_wait=video, nocolor=nocolor, loglevel=loglevel) elif session_type == 'vagrant': if vagrant_session_name is None: vagrant_session_name = 'shutit' + shutit_util.random_id() if isinstance(vagrant_num_machines, int): vagrant_num_machines = str(vagrant_num_machines) assert isinstance(vagrant_num_machines, str) assert isinstance(int(vagrant_num_machines), int) if vagrant_root_folder is None: vagrant_root_folder = shutit_global.shutit_global_object.owd return create_session_vagrant(vagrant_session_name, vagrant_num_machines, vagrant_image, vagrant_provider, vagrant_gui, vagrant_memory, vagrant_swapsize, echo, walkthrough, nocolor, video, vagrant_version, vagrant_virt_method, vagrant_root_folder, vagrant_cpu, loglevel)