diff --git a/crm/admin/Makefile.am b/crm/admin/Makefile.am index 48d836a828..3b658f48e4 100644 --- a/crm/admin/Makefile.am +++ b/crm/admin/Makefile.am @@ -1,116 +1,116 @@ # # Copyright (C) 2004 Andrew Beekhof # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License # as published by the Free Software Foundation; either version 2 # of the License, or (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. # MAINTAINERCLEANFILES = Makefile.in INCLUDES = -I$(top_builddir)/include -I$(top_srcdir)/include \ -I$(top_builddir)/libltdl -I$(top_srcdir)/libltdl \ -I$(top_builddir)/linux-ha -I$(top_srcdir)/linux-ha \ -I$(top_builddir) -I$(top_srcdir) hasbindir = $(sbindir) LIBRT = @LIBRT@ AM_CFLAGS = @CFLAGS@ $(CRM_DEBUG_FLAGS) COMMONLIBS = $(CRM_DEBUG_LIBS) \ $(top_builddir)/lib/clplumbing/libplumb.la \ $(top_builddir)/lib/pils/libpils.la \ $(top_builddir)/lib/crm/common/libcrmcommon.la \ $(top_builddir)/lib/crm/cib/libcib.la \ $(top_builddir)/lib/apphb/libapphb.la \ $(top_builddir)/lib/hbclient/libhbclient.la \ $(GLIBLIB) \ $(CURSESLIBS) \ $(LIBRT) -hasbin_SCRIPTS = crm_primitive.py +hasbin_PYTHON = crm_primitive.py cluster.py crm_utils.py crm_commands.py ## binary progs hasbin_PROGRAMS = crmadmin cibadmin ccm_tool crm_diff crm_mon iso8601 \ crm_master crm_standby crm_failcount crm_attribute \ crm_resource crm_verify crm_uuid ## SOURCES #noinst_HEADERS = config.h control.h crmd.h noinst_HEADERS = crmadmin_SOURCES = crmadmin.c crmadmin_CFLAGS = -DHA_VARLIBDIR='"@HA_VARLIBDIR@"' crmadmin_LDADD = $(COMMONLIBS) \ $(top_builddir)/lib/crm/pengine/libpe_status.la crm_uuid_SOURCES = crm_uuid.c crm_uuid_CFLAGS = -DHA_VARLIBDIR='"@HA_VARLIBDIR@"' crm_uuid_LDADD = $(GLIBLIB) $(top_builddir)/lib/clplumbing/libplumb.la cibadmin_SOURCES = cibadmin.c cibadmin_CFLAGS = -DHA_VARLIBDIR='"@HA_VARLIBDIR@"' cibadmin_LDADD = $(COMMONLIBS) ccm_tool_SOURCES = ccm_epoche.c ccm_tool_CFLAGS = -DHA_VARLIBDIR='"@HA_VARLIBDIR@"' ccm_tool_LDADD = $(COMMONLIBS) \ $(top_builddir)/membership/ccm/libccmclient.la crm_diff_SOURCES = xml_diff.c crm_diff_CFLAGS = -DHA_VARLIBDIR='"@HA_VARLIBDIR@"' crm_diff_LDADD = $(COMMONLIBS) crm_mon_SOURCES = crm_mon.c crm_mon_CFLAGS = -DHA_VARLIBDIR='"@HA_VARLIBDIR@"' crm_mon_LDADD = $(COMMONLIBS) \ $(top_builddir)/lib/crm/pengine/libpe_status.la # Arguments could be made that this should live in crm/pengine crm_verify_SOURCES = crm_verify.c crm_verify_CFLAGS = -DHA_VARLIBDIR='"@HA_VARLIBDIR@"' crm_verify_LDADD = $(COMMONLIBS) \ $(top_builddir)/lib/crm/pengine/libpe_status.la \ $(top_builddir)/crm/pengine/libpengine.la crm_master_SOURCES = crm_attribute.c crm_master_CFLAGS = -DHA_VARLIBDIR='"@HA_VARLIBDIR@"' crm_master_LDADD = $(COMMONLIBS) crm_standby_SOURCES = crm_attribute.c crm_standby_CFLAGS = -DHA_VARLIBDIR='"@HA_VARLIBDIR@"' crm_standby_LDADD = $(COMMONLIBS) crm_attribute_SOURCES = crm_attribute.c crm_attribute_CFLAGS = -DHA_VARLIBDIR='"@HA_VARLIBDIR@"' crm_attribute_LDADD = $(COMMONLIBS) crm_failcount_SOURCES = crm_attribute.c crm_failcount_CFLAGS = -DHA_VARLIBDIR='"@HA_VARLIBDIR@"' crm_failcount_LDADD = $(COMMONLIBS) crm_resource_SOURCES = crm_resource.c crm_resource_CFLAGS = -DHA_VARLIBDIR='"@HA_VARLIBDIR@"' crm_resource_LDADD = $(COMMONLIBS) \ $(top_builddir)/lib/crm/pengine/libpe_status.la iso8601_SOURCES = test.iso8601.c iso8601_CFLAGS = -DHA_VARLIBDIR='"@HA_VARLIBDIR@"' iso8601_LDADD = $(COMMONLIBS) clean-generic: rm -f *.log *.debug *.xml *~ install-exec-local: uninstall-local: diff --git a/crm/admin/cluster.py b/crm/admin/cluster.py new file mode 100644 index 0000000000..8a5fdff1c0 --- /dev/null +++ b/crm/admin/cluster.py @@ -0,0 +1,350 @@ +#!/bin/env python +# +# $Id: cluster.py,v 1.1 2006/08/14 08:37:54 andrew Exp $ +# +# pingd OCF Resource Agent +# Records (in the CIB) the current number of ping nodes a +# cluster node can connect to. +# +# Copyright (c) 2006 Andrew Beekhof +# All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of version 2 of the GNU General Public License as +# published by the Free Software Foundation. +# +# This program is distributed in the hope that it would be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# +# Further, this software is distributed without any warranty that it is +# free of the rightful claim of any third person regarding infringement +# or the like. Any license provided herein, whether implied or +# otherwise, applies only to this software file. Patent licenses, if +# any, provided herein do not apply to combinations of this program with +# other software, or any other product whatsoever. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write the Free Software Foundation, +# Inc., 59 Temple Place - Suite 330, Boston MA 02111-1307, USA. +# +####################################################################### + +import os +import sys +import readline +import traceback +from popen2 import Popen3 + +# Module copied from Mercurial +import crm_utils as utl +import crm_commands as crm + +class ParseError(Exception): + """Exception raised on errors in parsing the command line.""" +class QuotingError(Exception): + """Exception raised on errors in parsing the command line.""" +class UnknownCommand(Exception): + """Exception raised if command is not in the command table.""" +class AmbiguousCommand(Exception): + """Exception raised if command shortcut matches more than one command.""" + +table = { + "^help": ( + crm.help, None, [('v', 'verbose', None, 'extra information')], + "[-v]"), + "^up": (crm.up, None, [], None, "Move up a level in the heirarchy"), + "^crm": (crm.cd_, ["crm"], [], None), + "^nodes": (crm.cd_, ["crm"], [], None), + "^resources": (crm.cd_, ["crm"], [], None), + "^config": (crm.cd_, ["crm"], [], None), + "^list": ( + crm.do_list, ["nodes", "resources"], [('t', 'topic', "", '')], + None), + "^status": ( + crm.do_status, ["nodes", "resources"], [], + "[list of objects]"), + "^debug": (crm.toggle_flag, None, [('f', 'flag', 'debug', '')], None, "Toggle debugging information"), + "^exit": (crm.exit, None, [], "exit"), + "^debugstate": ( + crm.debugstate, None, [], + None, + "Dump information about the internal state of the program"), + } + +global_opt_table = [ + ('q', 'quiet', None, 'suppress output'), + ('v', 'verbose', None, 'enable additional output'), + ('', 'debug', None, 'enable debugging output'), + ('', 'devlog', None, 'enable developmental debugging output'), + ('', 'debugger', None, 'start debugger'), + ('', 'lsprof', None, 'print improved command execution profile'), + ('', 'traceback', None, 'print traceback on exception'), + ('', 'time', None, 'time how long the command takes'), + ('', 'profile', None, 'print command execution profile'), + ('', 'version', None, 'output version information and exit'), + ('i', 'interactive', None, 'run in interactive mode'), + ('h', 'help', None, 'display help and exit'), + ] + +def help_(text): + if text == "short": + utl.log_info("cluster.py [global options] [topic [topic...]] [command]") + return + if text: + choice = findpossible(text) + for key in choice.keys(): + alias, e = choice[key] + if e: + sub_cmd="" + if len(e) > 4: + utl.log_info("\n"+e[4]+"\n") + possible = findpossible("", alias[0]).keys() + possible.remove("up") + possible.remove("help") + possible.remove("exit") + if possible: + sub_cmd=' ('+'|'.join(possible)+')' + if e[3]: + utl.log_info("Usage: %s %s%s" % (alias[0], e[3], sub_cmd)) + else: + utl.log_info("Usage: %s%s" % (alias[0], sub_cmd)) + if choice: + return; + utl.log_err("No help text available for: %s" % text) + + +# Stolen from Mercurial commands.py + +def findpossible(cmd, topic=None): + """ + Return cmd -> (aliases, command table entry) + for each matching command. + Return debug commands (or their aliases) only if no normal command matches. + """ + if not topic: + topic = utl.crm_topic + + #utl.log_debug("Looking for completions in %s" % topic) + choice = {} + debugchoice = {} + for e in table.keys(): + t = table[e] + #utl.log_debug("Looking for "+topic +" in "+repr(t[1])) + if t[1] and topic not in t[1]: + continue + aliases = e.lstrip("^").split("|") + found = None + if cmd in aliases: + found = cmd + else: + for a in aliases: + if a.startswith(cmd): + found = a + break + if found is not None: + if aliases[0].startswith("debug"): + debugchoice[found] = (aliases, table[e]) + else: + choice[found] = (aliases, table[e]) + + if not choice and debugchoice: + choice = debugchoice + + return choice + +def findcmd(cmd): + """Return (aliases, command table entry) for command string.""" + choice = findpossible(cmd) + + if choice.has_key(cmd): + return choice[cmd] + + if len(choice) > 1: + clist = choice.keys() + clist.sort() + raise AmbiguousCommand(cmd, clist) + + if choice: + return choice.values()[0] + + raise UnknownCommand(cmd) + +def find_completer(start, i): + choice = findpossible(start) + if not choice: + return None + elif len(choice.keys()) < i: + return None + return choice.keys()[i] + +def parse(args): + options = {} + cmdoptions = {} + + try: + args = utl.fancyopts(args, global_opt_table, options) + except utl.getopt.GetoptError, inst: + raise ParseError(None, inst) + + if args: + cmd, args = args[0], args[1:] + aliases, i = findcmd(cmd) + cmd = aliases[0] + defaults = [] + if defaults: + args = defaults.split() + args + c = list(i[2]) + else: + cmd = None + c = [] + + # combine global options into local + for o in global_opt_table: + c.append((o[0], o[1], options[o[1]], o[3])) + + try: + args = utl.fancyopts(args, c, cmdoptions) + except utl.getopt.GetoptError, inst: + raise ParseError(cmd, inst) + + # separate global options back out + for o in global_opt_table: + n = o[1] + options[n] = cmdoptions[n] + del cmdoptions[n] + + utl.log_dev("args: %s\ncmdoptions: %s" + % (repr(args), repr(cmdoptions))) + return (cmd, cmd and i[0] or None, args, options, cmdoptions) + +def main_loop(args): + global global_opts + cmd = None + cmd_args = [] + cmdoptions = {} + + if not args: + return 0 + + try: + utl.log_dev("Loop Input: "+repr(args)) + cmd, func, new_args, ignore, new_cmdoptions = parse(args) + + cmd_args.extend(new_args) + utl.log_dev(repr(cmd_args)) + cmdoptions.update(new_cmdoptions) + + if func == crm.cd_: + cmdoptions["topic"] = cmd + + if not cmd: + utl.log_err(repr(args)) + cmd = "_unknown_" + raise UnknownCommand(None, "") + else: + d = lambda: func(*cmd_args, **cmdoptions) + return d() + + except crm.HelpRequest, inst: + help_(inst.args[0]) + return 0 + + except crm.ReparseRequest: + return main_loop(cmd_args) + + except ParseError, inst: + if inst.args[0]: + utl.log_err("%s: %s\n" % (inst.args[0], inst.args[1])) + help_(inst.args[0]) + else: + utl.log_err("%s\n" % inst.args[1]) + help_('short') + + except AmbiguousCommand, inst: + utl.log_info("%s: command '%s' is ambiguous:\n %s\n" + % (" ".join(utl.topic_stack), inst.args[0], " ".join(inst.args[1]))) + + except UnknownCommand, inst: + utl.log_err("%s: unknown command '%s'\n" % (" ".join(utl.topic_stack), inst.args[0])) + help_(utl.crm_topic) + + except IOError, inst: + if hasattr(inst, "code"): + utl.log_err("abort: %s\n" % inst) + elif hasattr(inst, "reason"): + utl.log_err("abort: error: %s\n" % inst.reason[1]) + elif hasattr(inst, "args"): + utl.log_err("broken pipe\n") + elif getattr(inst, "strerror", None): + if getattr(inst, "filename", None): + utl.log_err("abort: %s - %s\n" % (inst.strerror, inst.filename)) + else: + utl.log_err("abort: %s\n" % inst.strerror) + else: + raise + except OSError, inst: + if hasattr(inst, "filename"): + utl.log_err("abort: %s: %s\n" % (inst.strerror, inst.filename)) + else: + utl.log_err("abort: %s\n" % inst.strerror) + + except TypeError, inst: + # was this an argument error? + tb = traceback.extract_tb(sys.exc_info()[2]) + if len(tb) > 2: # no + raise + utl.log_err((inst, "\n")) + utl.log_err(("%s: invalid arguments\n" % cmd)) + help_(cmd) + raise + except SystemExit, inst: + # Exit gracefully + utl.exit_(inst.code) + + except: + utl.log_err("** unknown exception encountered, details follow\n") + raise + + return -1 + +args = sys.argv[1:] +utl.init_readline(find_completer) +try: + cmd, f_ignore, a_ignore, utl.global_opts, o_ignore = parse(args) +except: + pass + +if len(sys.argv) == 1: + utl.global_opts["interactive"] = 1 +elif not utl.global_opts.has_key("interactive"): + utl.global_opts["interactive"] = 0 + +while True: + rc = 0 + if args: + utl.set_topic(utl.topic_stack[-1]) + rc = main_loop(args) + utl.log_debug("rc: %s" % (repr(rc))) + + if not utl.global_opts["interactive"]: + utl.exit_(rc) + + try: + text = raw_input(" ".join(utl.topic_stack) +" # ") + args = utl.create_argv(text) + + except QuotingError, inst: + if inst.args[1]: + utl.log_err("%s. Found tokens: %s\n" % (inst.args[0], inst.args[1])) + else: + utl.log_err("%s\n" % inst.args[0]) + + except KeyboardInterrupt: + utl.exit_(0) + + except EOFError: + utl.exit_(0) + + + diff --git a/crm/admin/crm_commands.py b/crm/admin/crm_commands.py new file mode 100644 index 0000000000..d49466656e --- /dev/null +++ b/crm/admin/crm_commands.py @@ -0,0 +1,133 @@ +# +# $Id: crm_commands.py,v 1.1 2006/08/14 08:37:54 andrew Exp $ +# +# pingd OCF Resource Agent +# Records (in the CIB) the current number of ping nodes a +# cluster node can connect to. +# +# Copyright (c) 2006 Andrew Beekhof +# All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of version 2 of the GNU General Public License as +# published by the Free Software Foundation. +# +# This program is distributed in the hope that it would be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# +# Further, this software is distributed without any warranty that it is +# free of the rightful claim of any third person regarding infringement +# or the like. Any license provided herein, whether implied or +# otherwise, applies only to this software file. Patent licenses, if +# any, provided herein do not apply to combinations of this program with +# other software, or any other product whatsoever. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write the Free Software Foundation, +# Inc., 59 Temple Place - Suite 330, Boston MA 02111-1307, USA. +# +####################################################################### + +import crm_utils as utl + +class HelpRequest(Exception): + """Exception raised when a help listing is required.""" + +class ReparseRequest(Exception): + """Exception raised when a command changed the command-line.""" + +def up(*args, **cmdoptions): + l = len(topic_stack) + if l > 1: + topic_stack.pop() + utl.set_topic(topic_stack[-1]) + else: + utl.log_debug("Already at the top of the stack") + +def toggle_flag(*args, **cmdoptions): + flag = cmdoptions["flag"] + if utl.global_opts[flag]: + utl.global_opts[flag] = 0 + else: + utl.global_opts[flag] = 1 + + return utl.global_opts[flag] + +def cd_(*args, **cmdoptions): + global crm_topic + if not cmdoptions["topic"]: + utl.log_err("No topic specified") + return 1 + + if cmdoptions["topic"]: + utl.set_topic(cmdoptions["topic"]) + if args: + raise ReparseRequest() + if utl.crm_topic not in utl.topic_stack: + utl.topic_stack.append(cmdoptions["topic"]) + if not utl.global_opts["interactive"]: + help(cmdoptions["topic"]) + return 0 + +def exit(*args, **cmdoptions): + sys.exit(0) + +def help(*args, **cmdoptions): + if args: + raise HelpRequest(args[0]) + raise HelpRequest(utl.crm_topic) + +def debugstate(*args, **cmdoptions): + utl.log_info("Global Options: ") + for opt in utl.global_opts.keys(): + utl.log_info(" * %s:\t%s" % (opt, utl.global_opts[opt])) + utl.log_info("Stack: "+repr(utl.topic_stack)) + utl.log_info("Stack Head: "+utl.crm_topic) + return 0 + +def do_list(*args, **cmdoptions): + topic = utl.crm_topic + if cmdoptions.has_key("topic") and cmdoptions["topic"]: + topic = cmdoptions["topic"] + + utl.log_debug("Complete '%s' listing" % topic) + if topic == "resources": + utl.os_system("crm_resource -l", True) + elif topic == "nodes": + lines = utl.os_system("cibadmin -Q -o nodes", False) + for line in lines: + if line.find("node ") >= 0: + print line.rstrip() + else: + utl.log_err("%s: Topic %s is not (yet) supported" % ("list", topic)) + return 1 + return 0 + +def do_status(*args, **cmdoptions): + topic = utl.crm_topic + if cmdoptions.has_key("topic") and cmdoptions["topic"]: + topic = cmdoptions["topic"] + + if topic == "resources": + if not args: + utl.os_system("crm_resource -L", True) + for rsc in args: + utl.os_system("crm_resource -W -r %s"%rsc, True) + + elif topic == "nodes": + lines = utl.os_system("cibadmin -Q -o status", False) + for line in lines: + line = line.rstrip() + utl.log_dev("status line: "+line) + if line.find("node_state ") >= 0: + if not args: + print line + for node in args: + if line.find(node) >= 0: + print line + else: + utl.log_err("Topic %s is not (yet) supported" % topic) + return 1 + + return 0 diff --git a/crm/admin/crm_utils.py b/crm/admin/crm_utils.py new file mode 100644 index 0000000000..c1d6b3ed8f --- /dev/null +++ b/crm/admin/crm_utils.py @@ -0,0 +1,189 @@ +#!/bin/env python +# +# $Id: crm_utils.py,v 1.1 2006/08/14 08:37:54 andrew Exp $ +# +# pingd OCF Resource Agent +# Records (in the CIB) the current number of ping nodes a +# cluster node can connect to. +# +# Copyright (c) 2006 Andrew Beekhof +# All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of version 2 of the GNU General Public License as +# published by the Free Software Foundation. +# +# This program is distributed in the hope that it would be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# +# Further, this software is distributed without any warranty that it is +# free of the rightful claim of any third person regarding infringement +# or the like. Any license provided herein, whether implied or +# otherwise, applies only to this software file. Patent licenses, if +# any, provided herein do not apply to combinations of this program with +# other software, or any other product whatsoever. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write the Free Software Foundation, +# Inc., 59 Temple Place - Suite 330, Boston MA 02111-1307, USA. +# +####################################################################### + +import os +import sys +import getopt +import readline +import traceback +from popen2 import Popen3 + +crm_topic = "crm" +topic_stack = [ crm_topic ] +hist_file = os.environ.get('HOME')+"/.crm_history" +global_opts = {} + +def exit_(code=0): + if global_opts["interactive"]: + log_info("Exiting... ") + try: + readline.write_history_file(hist_file) + log_debug("Wrote history to: "+hist_file) + except: + log_debug("Couldnt write history to: "+hist_file) + sys.exit(code) + +def log_debug(log): + if global_opts.has_key("debug") and global_opts["debug"]: + print log + +def log_dev(log): + if global_opts.has_key("devlog") and global_opts["devlog"]: + print log + +def log_info(log): + print log + +def log_err(log): + print "ERROR: "+log + +def set_topic(name): + global crm_topic + if crm_topic != name: + log_dev("topic: %s->%s" % (crm_topic, name)) + crm_topic = name + +def os_system(cmd, print_raw=False): + log_debug("Performing command: "+cmd) + p = Popen3(cmd, None) + p.tochild.close() + result = p.fromchild.readlines() + p.fromchild.close() + p.wait() + if print_raw: + for line in result: + print line.rstrip() + return result + +# +# Creates an argv-style array (that preserves quoting) for use in shell-mode +# +def create_argv(text): + args = [] + word = [] + index = 0 + total = len(text) + + in_word = False + in_verbatum = False + + while index < total: + finish_word = False + append_word = False + #log_debug("processing: "+text[index]) + if text[index] == '\\': + index = index +1 + append_word = True + + elif text[index].isspace(): + if in_verbatum or in_word: + append_word = True + else: + finish_word = True + + elif text[index] == '"': + if in_verbatum: + append_word = True + else: + finish_word = True + if in_word: + in_word = False + else: + in_word = True + + elif text[index] == '\'': + finish_word = True + if in_verbatum: + in_verbatum = False + else: + in_verbatum = True + else: + append_word = True + + if finish_word: + if word: + args.append(''.join(word)) + word = [] + elif append_word: + word.append(text[index]) + #log_debug("Added %s to word: %s" % (text[index], str(word))) + + index = index +1 + + if in_verbatum or in_word: + text="" + if word: + text=" after: '%s'"%''.join(word) + raise QuotingError("Un-matched quoting%s"%text, args) + + elif word: + args.append(''.join(word)) + + return args + +def init_readline(func): + readline.set_completer(func) + readline.parse_and_bind("tab: complete") + readline.set_history_length(100) + + try: + readline.read_history_file(hist_file) + except: + pass + +def fancyopts(args, options, state): + long = [] + short = '' + map = {} + dt = {} + + for s, l, d, c in options: + pl = l.replace('-', '_') + map['-'+s] = map['--'+l] = pl + state[pl] = d + dt[pl] = type(d) + if not d is None and not callable(d): + if s: s += ':' + if l: l += '=' + if s: short = short + s + if l: long.append(l) + + opts, args = getopt.getopt(args, short, long) + + for opt, arg in opts: + if dt[map[opt]] is type(fancyopts): state[map[opt]](state,map[opt],arg) + elif dt[map[opt]] is type(1): state[map[opt]] = int(arg) + elif dt[map[opt]] is type(''): state[map[opt]] = arg + elif dt[map[opt]] is type([]): state[map[opt]].append(arg) + elif dt[map[opt]] is type(None): state[map[opt]] = 1 + + return args