def __init__(self, hosts=[], envSettings={}, alias=None, aliases=None, default=False, name=None, *args, **kwargs): if type(hosts) is str: hosts = [hosts] from ccapi import Configurer Configurer.patch(self) print self.fabricLogStateOutput env.hosts = hosts env.warn_only = False env.exceptions = ['everything'] env.abort_exception = Exception for key, value in self.fabricLogStateOutput.items(): fabric.state.output[key] = bool(value) for key, value in envSettings.items(): if hasattr(env, key): setattr(env, key, value) Task.__init__(self, alias, aliases, default, name, *args, **kwargs)
def __init__(self, name, desc='', tasks=None): self.name = name Task.__init__(self) self.todo = [] if tasks is not None: for t in tasks: self.add_task(t) if desc: desc += " " self.__doc__ = desc if self.todo: self.__doc__ += "(%s)" % ", ".join([self.get_name(t) for t in self.todo])
def load_fabfile(path, importer=None): """ Import given fabfile path and return (docstring, callables). Specifically, the fabfile's ``__doc__`` attribute (a string) and a dictionary of ``{'name': callable}`` containing all callables which pass the "is a Fabric task" test. """ classic_tasks = None if importer is None: importer = __import__ # Get directory and fabfile name directory, fabfile = os.path.split(path) # If the directory isn't in the PYTHONPATH, add it so our import will work added_to_path = False index = None if directory not in sys.path: sys.path.insert(0, directory) added_to_path = True # If the directory IS in the PYTHONPATH, move it to the front temporarily, # otherwise other fabfiles -- like Fabric's own -- may scoop the intended # one. else: i = sys.path.index(directory) if i != 0: # Store index for later restoration index = i # Add to front, then remove from original position sys.path.insert(0, directory) del sys.path[i + 1] # Perform the import (trimming off the .py) imported = importer(os.path.splitext(fabfile)[0]) # Remove directory from path if we added it ourselves (just to be neat) if added_to_path: del sys.path[0] # Put back in original index if we moved it if index is not None: sys.path.insert(index + 1, directory) del sys.path[0] # Try to load classic-style tasks if no new-style tasks were auto-registered. if not Task.all(): _, classic_tasks = load_tasks_from_module(imported) # Clean up after ourselves _seen.clear() return imported.__doc__, classic_tasks
def name_to_task(name): t = Task() t.name = name return t
def test_base_task_raises_exception_on_call_to_run(): task = Task() task.run()
def test_base_task_provides_undefined_name(): task = Task() eq_("undefined", task.name)
def test_aliases_are_None_by_default(self): task = Task() self.assertTrue(task.aliases is None)
def test_aliases_are_set_based_on_provided_aliases(self): aliases = ["a_%d" % i for i in range(random.randint(1, 10))] task = Task(aliases=aliases) self.assertTrue(all([a in task.aliases for a in aliases]))
def test_takes_an_alias_kwarg_and_wraps_it_in_aliases_list(self): random_alias = "alias_%d" % random.randint(100, 200) task = Task(alias=random_alias) self.assertTrue(random_alias in task.aliases)
def main(): """ Main command-line execution loop. """ try: # Parse command line options parser, options, arguments = parse_options() # Handle regular args vs -- args arguments = parser.largs remainder_arguments = parser.rargs # Update env with any overridden option values # NOTE: This needs to remain the first thing that occurs # post-parsing, since so many things hinge on the values in env. for option in env_options: state.env[option.dest] = getattr(options, option.dest) # Handle --hosts, --roles, --exclude-hosts (comma separated string => list) for key in ['hosts', 'roles', 'exclude_hosts']: if key in state.env and isinstance(state.env[key], str): state.env[key] = state.env[key].split(',') # Handle output control level show/hide update_output_levels(show=options.show, hide=options.hide) # Handle version number option if options.show_version: print("Fabric %s" % state.env.version) sys.exit(0) # Handle case where we were called bare, i.e. just "fab", and print # a help message. actions = (options.list_commands, options.shortlist, options.display, arguments, remainder_arguments) if not any(actions): parser.print_help() sys.exit(1) # Load settings from user settings file, into shared env dict. state.env.update(load_settings(state.env.rcfile)) # Find local fabfile path or abort fabfile = find_fabfile() if not fabfile and not remainder_arguments: abort("Couldn't find any fabfiles!") # Store absolute path to fabfile in case anyone needs it state.env.real_fabfile = fabfile # Load fabfile (which calls its module-level code, including # tweaks to env values) and put its commands in the shared commands # dict if fabfile: docstring, classic_tasks = load_fabfile(fabfile) if classic_tasks: state.commands.update(classic_tasks) else: state.commands.update(Task.all()) # Abort if no commands found if not state.commands and not remainder_arguments: abort("Fabfile didn't contain any commands!") # Now that we're settled on a fabfile, inform user. if state.output.debug: if fabfile: print("Using fabfile '%s'" % fabfile) else: print("No fabfile loaded -- remainder command only") # Shortlist is now just an alias for the "short" list format; # it overrides use of --list-format if somebody were to specify both if options.shortlist: options.list_format = 'short' # List available commands if options.list_commands: print("\n".join(list_commands(docstring, options.list_format))) sys.exit(0) # Handle show (command-specific help) option if options.display: display_command(options.display) # If user didn't specify any commands to run, show help if not (arguments or remainder_arguments): parser.print_help() sys.exit(0) # Or should it exit with error (1)? # Parse arguments into commands to run (plus args/kwargs/hosts) commands_to_run = parse_arguments(arguments) # Parse remainders into a faux "command" to execute remainder_command = parse_remainder(remainder_arguments) # Figure out if any specified task names are invalid unknown_commands = [] for tup in commands_to_run: if crawl(tup[0], state.commands) is None: unknown_commands.append(tup[0]) # Abort if any unknown commands were specified if unknown_commands: abort("Command(s) not found:\n%s" \ % indent(unknown_commands)) # Generate remainder command and insert into commands, commands_to_run if remainder_command: r = '<remainder>' state.commands[r] = lambda: api.run(remainder_command) commands_to_run.append((r, [], {}, [], [], [])) if state.output.debug: names = ", ".join(x[0] for x in commands_to_run) print("Commands to run: %s" % names) # At this point all commands must exist, so execute them in order. for name, args, kwargs, cli_hosts, cli_roles, cli_exclude_hosts in commands_to_run: # Get callable by itself task = crawl(name, state.commands) # Set current task name (used for some error messages) state.env.command = name # Set host list (also copy to env) state.env.all_hosts = hosts = get_hosts( task, cli_hosts, cli_roles, cli_exclude_hosts) # If hosts found, execute the function on each host in turn for host in hosts: # Preserve user prev_user = state.env.user # Split host string and apply to env dict username, hostname, port = interpret_host_string(host) # Log to stdout if state.output.running: print("[%s] Executing task '%s'" % (host, name)) # Actually run command _run_task(task, args, kwargs) # Put old user back state.env.user = prev_user # If no hosts found, assume local-only and run once if not hosts: _run_task(task, args, kwargs) # If we got here, no errors occurred, so print a final note. if state.output.status: print("\nDone.") except SystemExit: # a number of internal functions might raise this one. raise except KeyboardInterrupt: if state.output.status: print >> sys.stderr, "\nStopped." sys.exit(1) except: sys.excepthook(*sys.exc_info()) # we might leave stale threads if we don't explicitly exit() sys.exit(1) finally: disconnect_all() sys.exit(0)