# Parse a Steuermann Spec file # # This is an exyapps grammer in specfile.exy from . import nodes %% parser specfile: ignore: "[ \r\t\n]+" ignore: "#.*\n" token END: "$" token HOSTGROUP: "HOSTGROUP" token IF: "IF" token IFNOT: "IFNOT" token TABLE: "TABLE" token HOST : "HOST" token CMD: "CMD" token OPT: "OPT" token AFTER: "AFTER" token RUN: "RUN" token LOCAL: "LOCAL" token IMPORT: "IMPORT" token RESOURCE: "RESOURCE" token DEBUG: "DEBUG" token name: "[a-zA-Z0-9_.-]+" token STAR: "\*" token cmdname: "[a-zA-Z0-9_.-]+" token tablename: "[a-zA-Z0-9_.-]+" token wildname: "[@]{0,1}[*?a-zA-Z0-9_.-]+" token string: '"[^"]*"' token SLASH: "/" token COLON: ":" token hostgroup: "@[a-zA-Z0-9_.-]+" token number: "[0-9]+" token RES_ALL: "all" token RES_AVAILABLE: "available" # watch carefully: none of the keywords are "END" on a line by themselves token CONDITIONS: 'CONDITIONS[^"]*\n[\s]*END[ \t]*\n' ## # This is the whole file rule start: table_list "$" {{ return table_list }} ## # rule table_list: table_section + ## # rule table_section: DEBUG string {{ print("-->debug: %s"%string) }} | hostgroup_def | CONDITIONS {{ nodes.declare_conditions( CONDITIONS, self._scanner.filename ) }} | TABLE tablename {{ table_name = tablename }} HOST {{ hostlist = [ ] }} ( name {{ hostlist.append(name) }} | hostgroup {{ hostlist = hostlist + nodes.get_hostgroup( hostgroup ) }} )+ cond_command<> + | IMPORT string {{ self.data.import_list.append( string[1:-1] ) }} ## # rule hostgroup_def : HOSTGROUP hostgroup {{ nodes.define_hostgroup( hostgroup) }} ( hostgroup_front<> hostgroup_back<> )+ ## # rule hostgroup_front<> : IF name {{ return nodes.check_condition(name, self._scanner.filename ) }} | IFNOT name {{ return not nodes.check_condition(name, self._scanner.filename ) }} | {{ return True }} ## # rule hostgroup_back<> : COLON ( name {{ if accept_nodes : nodes.add_hostgroup( hg, name ) }} | hostgroup {{ if accept_nodes : nodes.add_hostgroup( hg, hostgroup ) }} )* ## # rule cond_command<> : IF name COLON command {{ if nodes.check_condition(name, self._scanner.filename ) : self.data.add_command_list( table_name, hostlist, [ command ] ) }} | IFNOT name COLON command {{ if not nodes.check_condition(name, self._scanner.filename ) : self.data.add_command_list( table_name, hostlist, [ command ] ) }} | command {{ self.data.add_command_list( table_name, hostlist, [ command ] ) }} ## # a single command, including any number of AFTER clauses rule command: CMD {{ cmd_pos = self._scanner.get_pos() }} {{ script_type = 'l' }} {{ resources = { 'cpu' : 1 } }} cmdname {{ cmd_name=cmdname; script=cmdname; x_after_clause = [ ] }} [ RUN string {{ script = string[1:-1]; script_type='r' }} | LOCAL string {{ script = string[1:-1]; script_type='l' }} ] ( {{ after_pos = self._scanner.get_pos() }} [ RESOURCE resource_defs {{ resources.update(resource_defs) }} ] AFTER optword after_spec {{ x_after_clause.append( (after_spec, optword, after_pos) ) }} )* {{ return ( cmd_name, script, script_type, x_after_clause, cmd_pos, resources ) }} ## # rule resource_defs: {{ rl = { } }} ( name "=" ( number {{ ans = int(number) }} | RES_ALL {{ ans = 'all' }} | RES_AVAILABLE {{ ans = 'available' }} ) {{ rl[name] = ans }} ) * {{ print(rl) ; return rl }} ## # in the AFTER clause, you can say OPT to mean the node is optional (not an error if it does not exist) rule optword: OPT {{ return 0 }} | {{ return 1 }} ## rule after_spec: wildname {{ rval = wildname }} [ COLON wildname {{ rval = rval + ':' + wildname }} ] [ SLASH wildname {{ rval = rval + '/' + wildname }} ] {{ return rval }} %%