def __init__(self, config): formatter = ResponseFormatter() ResponsePage.__init__(self, '', formatter) self.config = config # Regular Expression for matching variables in a path definition. # Currently only supports stuffing a single variable in a path # component. varmatch = re.compile(r'^%\((.*)\)s$') BINDIR = os.path.dirname(os.path.realpath(sys.argv[0])) tree = ET.parse(os.path.join(BINDIR, '..', 'etc', 'input.xml')) for command in tree.getiterator("command"): for transport in command.getiterator("transport"): if "name" not in command.attrib \ or "method" not in transport.attrib \ or "path" not in transport.attrib: continue name = command.attrib["name"] method = transport.attrib["method"] path = transport.attrib["path"] trigger = transport.attrib.get("trigger") container = self relative = "" # Traverse down the resource tree, container will # end up pointing to the correct spot. # Create branches and leaves as necessary, continueing to # traverse downward. for component in path.split("/"): relative = relative + "/" + component #log.msg("Working with component '" + component + "' of '" + relative + "'.") m = varmatch.match(component) # Is this piece of the path dynamic? if not m: #log.msg("Component '" + component + "' is static.") child = container.getStaticEntity(component) if child is None: #log.msg("Creating new static component '" + component + "'.") child = ResponsePage(relative, formatter) container.putChild(component, child) container = child else: #log.msg("Component '" + component + "' is dynamic.") path_variable = m.group(1) if container.dynamic_child is not None: #log.msg("Dynamic component '" + component + "' already exists.") current_variable = container.dynamic_child.\ path_variable if current_variable != path_variable: log.msg("Warning: Could not use variable '" + path_variable + "', already have " + "dynamic variable '" + current_variable + "'.") # XXX: Raise an error if they don't match container = container.dynamic_child else: #log.msg("Dynamic component '" + component + "' had correct variable.") container = container.dynamic_child else: #log.msg("Creating new dynamic component '" + component + "'.") child = ResponsePage(relative, formatter, path_variable=path_variable) container.dynamic_child = child container = child fullcommand = name if trigger: fullcommand = fullcommand + "_" + trigger mymodule = getattr(commands, fullcommand, None) if not mymodule: log.msg("No module available in aquilon.worker.commands " + "for %s" % fullcommand) # See commands/__init__.py for more info here... myinstance = getattr(mymodule, "broker_command", None) if not myinstance: log.msg("No class instance available for %s" % fullcommand) myinstance = BrokerCommand() myinstance.command = name rendermethod = method.upper() if container.handlers.get(rendermethod, None): log.msg("Warning: Already have a %s here at %s..." % (rendermethod, container.path)) #log.msg("Setting 'command_" + fullcommand + "' as '" + rendermethod + "' for container '" + container.path + "'.") container.handlers[rendermethod] = myinstance # Since we are parsing input.xml anyway, record the possible # parameters... for option in command.getiterator("option"): if "name" not in option.attrib: continue option_name = option.attrib["name"] if option_name not in myinstance.optional_parameters: myinstance.optional_parameters.append(option_name) if "type" in option.attrib: paramtype = option.attrib["type"] myinstance.parameter_types[option_name] = paramtype if paramtype == "int": myinstance.parameter_checks[ option_name] = force_int elif paramtype == "float": myinstance.parameter_checks[ option_name] = force_float elif paramtype == "boolean" or paramtype == "flag": myinstance.parameter_checks[ option_name] = force_boolean elif paramtype == "ipv4": myinstance.parameter_checks[ option_name] = force_ipv4 elif paramtype == "mac": myinstance.parameter_checks[ option_name] = force_mac elif paramtype == "json": myinstance.parameter_checks[ option_name] = force_json_dict elif paramtype == "string" or paramtype == "file": # Current twisted can't handle unicode output, so # do not allow non-ASCII input either myinstance.parameter_checks[ option_name] = force_ascii elif paramtype == "list": myinstance.parameter_checks[ option_name] = force_list else: # pragma: no cover log.msg("Warning: unknown option type %s" % paramtype) else: # pragma: no cover log.msg("Warning: argument type not known for %s.%s" % (myinstance.command, option_name)) pbt = myinstance.parameters_by_type for option_name, paramtype \ in myinstance.parameter_types.items(): if paramtype in pbt: pbt[paramtype].append(option_name) else: pbt[paramtype] = [option_name] cache_version(config) log.msg("Starting aqd version %s" % config.get("broker", "version")) self.make_required_dirs() def _logChildren(level, container): for (key, child) in container.listStaticEntities(): log.msg("Resource at level %d for %s [key:%s]" % (level, child.path, key)) _logChildren(level + 1, child) if getattr(container, "dynamic_child", None): log.msg("Resource at level %d for %s [dynamic]" % (level, container.dynamic_child.path)) _logChildren(level + 1, container.dynamic_child)
def __init__(self): """ Provides some convenient variables for commands. Also sets requires_* parameters for some sets of commands. All of the command objects are singletons (or Borg). """ self.dbf = DbFactory() self.config = Config() self.az = AuthorizationBroker() self.formatter = ResponseFormatter() self.catalog = StatusCatalog() # Force the instance to have local copies of the class defaults... # This allows resources.py to modify instances without worrying # about inheritance issues (classes sharing required or optional # parameters). self.required_parameters = self.required_parameters[:] self.optional_parameters = self.optional_parameters[:] # Parameter checks are filled in automatically based on input.xml. This # lets us do some rudimentary checks before the actual command is # invoked. self.parameter_checks = {} # The parameter types are filled in automatically based on input.xml. self.parameter_types = {} # This is the pivot of the above, filled in at the same time. It is a # dictionary of type names to lists of parameters. self.parameters_by_type = {} self.action = self.__module__ package_prefix = "aquilon.worker.commands." if self.action.startswith(package_prefix): self.action = self.action[len(package_prefix):] # self.command is set correctly in resources.py after parsing input.xml self.command = self.action # The readonly and format flags are done here for convenience # and simplicity. They could be overridden by the __init__ # method of any show/search/cat commands that do not want these # defaults. Some 'one-off' commands (like ping and status) # just set the variables themselves. if self.action.startswith("show") or self.action.startswith("search"): self.requires_readonly = True self.requires_format = True if self.action.startswith("cat"): self.requires_format = True self.requires_readonly = True self._is_lock_free = True if not self.requires_readonly \ and self.config.get('broker', 'mode') == 'readonly': self.badmode = 'readonly' else: self.badmode = False self._update_render(self.render) if not self.defer_to_thread: if self.requires_azcheck or self.requires_transaction: self.defer_to_thread = True log.msg("Forcing defer_to_thread to True because of " "required authorization or transaction for %s" % self.command) # Not sure how to handle formatting with deferred... self.requires_format = False