query_config
#!/usr/bin/env python3
"""
Displays information about available compsets, component settings, grids and/or
machines. Typically run with one of the arguments --compsets, --settings,
--grids or --machines; if you specify more than one of these arguments,
information will be listed for each.
"""
from Tools.standard_script_setup import *
import re
from CIME.utils import expect, get_model
from CIME.XML.files import Files
from CIME.XML.component import Component
from CIME.XML.compsets import Compsets
from CIME.XML.grids import Grids
#from CIME.XML.machines import Machines
import CIME.XML.machines
from argparse import RawTextHelpFormatter
logger = logging.getLogger(__name__)
supported_comp_interfaces = ["mct", "nuopc", "moab"]
def query_grids(files, long_output, xml=False):
"""
query all grids.
"""
config_file = files.get_value("GRIDS_SPEC_FILE")
expect(os.path.isfile(config_file),
"Cannot find config_file {} on disk".format(config_file))
grids = Grids(config_file)
if xml:
print("{}".format(grids.get_raw_record().decode("UTF-8")))
elif long_output:
grids.print_values(long_output=long_output)
else:
grids.print_values()
def query_machines(files, machine_name='all', xml=False):
"""
query machines. Defaule: all
"""
config_file = files.get_value("MACHINES_SPEC_FILE")
expect(os.path.isfile(config_file),
"Cannot find config_file {} on disk".format(config_file))
# Provide a special machine name indicating no need for a machine name
machines = Machines(config_file, machine="Query")
if xml:
if machine_name == 'all':
print("{}".format(machines.get_raw_record().decode("UTF-8")))
else:
machines.set_machine(machine_name)
print("{}".format(machines.get_raw_record(root=machines.machine_node).decode("UTF-8")))
else:
machines.print_values(machine_name=machine_name)
def query_compsets(files, name, xml=False):
"""
query compset definition give a compset name
"""
# Determine valid component values by checking the value attributes for COMPSETS_SPEC_FILE
components = get_compsets(files)
match_found = None
all_components = False
if re.search("^all$", name): # print all compsets
match_found = name
all_components = True
else:
for component in components:
if component == name:
match_found = name
break
# If name is not a valid argument - exit with error
expect(match_found is not None,
"Invalid input argument {}, valid input arguments are {}".format(name, components))
if all_components: # print all compsets
for component in components:
# the all_components flag will only print available components
print_compset(component, files, all_components=all_components, xml=xml)
else:
print_compset(name, files, xml=xml)
def print_compset(name, files, all_components=False, xml=False):
"""
print compsets associated with the component name, but if all_components is true only
print the details if the associated component is available
"""
# Determine the config_file for the target component
config_file = files.get_value("COMPSETS_SPEC_FILE", attribute={"component":name})
# only error out if we aren't printing all otherwise exit quitely
if not all_components:
expect((config_file),
"Cannot find any config_component.xml file for {}".format(name))
# Check that file exists on disk
expect(os.path.isfile(config_file),
"Cannot find config_file {} on disk".format(config_file))
elif config_file is None or not os.path.isfile(config_file):
return
if get_model() == 'ufs' and name == 'drv':
return
print("\nActive component: {}".format(name))
# Now parse the compsets file and write out the compset alias and longname as well as the help text
# determine component xml content
compsets = Compsets(config_file)
# print compsets associated with component without help text
if xml:
print("{}".format(compsets.get_raw_record().decode("UTF-8")))
else:
compsets.print_values(arg_help=False)
def query_all_components(files, xml=False):
"""
query all components
"""
components = get_components(files)
# Loop through the elements for each component class (in config_files.xml)
for comp in components:
string = "CONFIG_{}_FILE".format(comp)
# determine all components in string
components = files.get_components(string)
for item in components:
query_component(item, files, all_components=True, xml=xml)
def query_component(name, files, all_components=False, xml=False):
"""
query a component by name
"""
# Determine the valid component classes (e.g. atm) for the driver/cpl
# These are then stored in comps_array
components = get_components(files)
# Loop through the elements for each component class (in config_files.xml)
# and see if there is a match for the the target component in the component attribute
match_found = False
valid_components = []
config_exists = False
for comp in components:
string = "CONFIG_{}_FILE".format(comp)
config_file = None
# determine all components in string
root_dir_node_name = "COMP_ROOT_DIR_{}".format(comp)
components = files.get_components(root_dir_node_name)
if components is None:
components = files.get_components(string)
for item in components:
valid_components.append(item)
logger.debug ("{}: valid_components {}".format(comp, valid_components))
# determine if config_file is on disk
if name is None:
config_file = files.get_value(string)
elif name in valid_components:
config_file = files.get_value(string, attribute={"component":name})
logger.debug("query {}".format(config_file))
if config_file is not None:
match_found = True
config_exists = os.path.isfile(config_file)
break
if not all_components and not config_exists:
expect(config_exists,
"Cannot find config_file {} on disk".format(config_file))
elif all_components and not config_exists:
print("WARNING: Couldn't find config_file {} on disk".format(config_file))
return
# If name is not a valid argument - exit with error
expect(match_found,
"Invalid input argument {}, valid input arguments are {}".format(name, valid_components))
# Check that file exists on disk, if not exit with error
expect((config_file),
"Cannot find any config_component.xml file for {}".format(name))
# determine component xml content
component = Component(config_file, "CPL")
if xml:
print("{}".format(component.get_raw_record().decode("UTF-8")))
else:
component.print_values()
def parse_command_line(args, description):
"""
parse command line arguments
"""
cime_model = CIME.utils.get_model()
parser = ArgumentParser(description=description,
formatter_class=RawTextHelpFormatter)
CIME.utils.setup_standard_logging_options(parser)
valid_components = ['all']
parser.add_argument("--xml", action="store_true",
help="Output in xml format.")
files = {}
for comp_interface in supported_comp_interfaces:
files[comp_interface] = Files(comp_interface=comp_interface)
components = files[comp_interface].get_components("COMPSETS_SPEC_FILE")
for item in components:
valid_components.append(item)
parser.add_argument("--compsets", nargs='?', const='all', choices=valid_components,
help="Query compsets corresponding to the target component for the {} model."
" If no component is given, lists compsets defined by all components".format(cime_model))
# Loop through the elements for each component class (in config_files.xml)
valid_components = ['all']
tmp_comp_interfaces = supported_comp_interfaces
for comp_interface in tmp_comp_interfaces:
try:
components = get_components(files[comp_interface])
except Exception:
supported_comp_interfaces.remove(comp_interface)
for comp in components:
if cime_model == "cesm":
string = "COMP_ROOT_DIR_{}".format(comp)
else:
string = "CONFIG_{}_FILE".format(comp)
# determine all components in string
components = files[comp_interface].get_components(string)
if components:
for item in components:
valid_components.append(item)
parser.add_argument("--components", nargs='?', const='all', choices=valid_components,
help="Query component settings corresponding to the target component for {} model."
"\nIf the option is empty, then the lists settings defined by all components is output".format(cime_model))
parser.add_argument("--grids", action="store_true",
help="Query supported model grids for {} model.".format(cime_model))
# same for all comp_interfaces
config_file = files['mct'].get_value("MACHINES_SPEC_FILE")
expect(os.path.isfile(config_file),
"Cannot find config_file {} on disk".format(config_file))
machines = Machines(config_file, machine="Query")
machine_names = ['all', 'current']
machine_names.extend(machines.list_available_machines())
parser.add_argument("--machines", nargs='?', const='all', choices=machine_names,
help="Query supported machines for {} model."
"\nIf option is left empty then all machines are listed,"
"\nIf the option is 'current' then only the current machine details are listed.".format(cime_model))
parser.add_argument("--long", action="store_true",
help="Provide long output for queries")
parser.add_argument("--comp_interface", choices=supported_comp_interfaces,
default='mct',
help="Coupler/Driver interface")
args = CIME.utils.parse_args_and_handle_standard_logging_options(args, parser)
# make sure at least one argument has been passed
if not (args.grids or args.compsets or args.components or args.machines):
parser.print_help(sys.stderr)
return args.grids, args.compsets, args.components, args.machines, \
args.long, args.xml, files[args.comp_interface]
def get_compsets(files):
"""
Determine valid component values by checking the value attributes for COMPSETS_SPEC_FILE
"""
return files.get_components("COMPSETS_SPEC_FILE")
def get_components(files):
"""
Determine the valid component classes (e.g. atm) for the driver/cpl
These are then stored in comps_array
"""
infile = files.get_value("CONFIG_CPL_FILE")
config_drv = Component(infile, "CPL")
return config_drv.get_valid_model_components()
class ArgumentParser(argparse.ArgumentParser):
"""
we override the error message from ArgumentParser to have a more helpful
message in the case of missing arguments
"""
def error(self, message):
self.print_usage(sys.stderr)
# missing argument
# TODO: assumes comp_interface='mct'
if "expected one argument" in message:
if "compset" in message:
components = get_compsets(Files(comp_interface='mct'))
self.exit(2, '{}: error: {}\nValid input arguments are {}\n'
.format(self.prog, message, components))
elif "component" in message:
files = Files(comp_interface='mct')
components = get_components(files)
# Loop through the elements for each component class (in config_files.xml)
valid_components = []
for comp in components:
string = "CONFIG_{}_FILE".format(comp)
# determine all components in string
components = files.get_components(string)
for item in components:
valid_components.append(item)
self.exit(2, '{}: error: {}\nValid input arguments are {}\n'
.format(self.prog, message, valid_components))
# for all other errors
self.exit(2, '{}: error: {}\n'.format(self.prog, message))
class Machines(CIME.XML.machines.Machines):
"""
we overide print_values from Machines to add current in machine description
"""
def print_values(self, machine_name='all'): # pylint: disable=arguments-differ
# set flag to look for single machine
if 'all' not in machine_name:
single_machine = True
if machine_name == 'current':
machine_name = self.probe_machine_name(warn=False)
else:
single_machine = False
# if we can't find the specified machine
if single_machine and machine_name is None:
files = Files()
config_file = files.get_value("MACHINES_SPEC_FILE")
print("Machine is not listed in config file: {}".format(config_file))
else: # write out machines
if single_machine:
machine_names = [machine_name]
else:
machine_names = self.list_available_machines()
print("Machine(s)\n")
for name in machine_names:
self.set_machine(name)
desc = self.text(self.get_child("DESC"))
os_ = self.text(self.get_child("OS"))
compilers = self.text(self.get_child("COMPILERS"))
mpilibnodes = self.get_children("MPILIBS", root=self.machine_node)
mpilibs = []
for node in mpilibnodes:
mpilibs.extend(self.text(node).split(','))
# This does not include the possible depedancy of mpilib on compiler
# it simply provides a list of mpilibs available on the machine
mpilibs = list(set(mpilibs))
max_tasks_per_node = self.text(self.get_child("MAX_TASKS_PER_NODE"))
mpitasks_node = self.get_optional_child("MAX_MPITASKS_PER_NODE")
max_mpitasks_per_node = self.text(mpitasks_node) if mpitasks_node else max_tasks_per_node
current_machine = self.probe_machine_name(warn=False)
name += " (current)" if current_machine and current_machine in name else ""
print(" {} : {} ".format(name, desc))
print(" os ", os_)
print(" compilers ",compilers)
print(" mpilibs ",mpilibs)
if max_mpitasks_per_node is not None:
print(" pes/node ",max_mpitasks_per_node)
if max_tasks_per_node is not None:
print(" max_tasks/node ",max_tasks_per_node)
print('')
def _main_func(description):
"""
main function
"""
grids, compsets, components, machines, long_output, xml, files = parse_command_line(sys.argv, description)
if grids:
query_grids(files, long_output, xml=xml)
if compsets is not None:
query_compsets(files, name=compsets, xml=xml)
if components is not None:
if re.search("^all$", components): # print all compsets
query_all_components(files, xml=xml)
else:
query_component(components, files, xml=xml)
if machines is not None:
query_machines(files, machine_name=machines, xml=xml)
# main entry point
if __name__ == "__main__":
_main_func(__doc__)