from sfa.util.version import version_tag
from sfa.util.config import Config
+
def validator(validated_variables):
pass
# maint_user = validated_variables["PLC_API_MAINTENANCE_USER"]
# root_user = validated_variables["PLC_ROOT_USER"]
# if maint_user == root_user:
-# errStr="PLC_API_MAINTENANCE_USER=%s cannot be the same as PLC_ROOT_USER=%s"%(maint_user,root_user)
+# errStr="PLC_API_MAINTENANCE_USER=%s cannot be the same as PLC_ROOT_USER=%s"%(maint_user, root_user)
# raise plc_config.ConfigurationException(errStr)
usual_variables = [
"SFA_GENERIC_FLAVOUR",
"SFA_INTERFACE_HRN",
"SFA_REGISTRY_ROOT_AUTH",
- "SFA_REGISTRY_HOST",
+ "SFA_REGISTRY_HOST",
"SFA_AGGREGATE_HOST",
"SFA_SM_HOST",
"SFA_DB_HOST",
- ]
-
-flavour_xml_section_hash = { \
- 'pl':'sfa_plc',
- 'openstack':'sfa_nova',
- 'fd':'sfa_federica',
- 'nitos':'sfa_nitos'
- }
-configuration={ \
- 'name':'sfa',
- 'service':"sfa",
- 'usual_variables':usual_variables,
- 'config_dir':"/etc/sfa",
- 'validate_variables':{},
- 'validator':validator,
- }
+]
+
+flavour_xml_section_hash = {
+ 'pl': 'sfa_plc',
+ 'openstack': 'sfa_nova',
+ 'fd': 'sfa_federica',
+ 'nitos': 'sfa_nitos',
+ 'dummy': 'sfa_dummy',
+}
+configuration = {
+ 'name': 'sfa',
+ 'service': "sfa",
+ 'usual_variables': usual_variables,
+ 'config_dir': "/etc/sfa",
+ 'validate_variables': {},
+ 'validator': validator,
+}
# GLOBAL VARIABLES
#
-g_configuration=None
-usual_variables=None
-config_dir=None
-service=None
+g_configuration = None
+usual_variables = None
+config_dir = None
+service = None
+
def noop_validator(validated_variables):
pass
# historically we could also configure the devel pkg....
-def init_configuration ():
+
+
+def init_configuration():
global g_configuration
global usual_variables, config_dir, service
- usual_variables=g_configuration["usual_variables"]
- config_dir=g_configuration["config_dir"]
- service=g_configuration["service"]
+ usual_variables = g_configuration["usual_variables"]
+ config_dir = g_configuration["config_dir"]
+ service = g_configuration["service"]
global def_default_config, def_site_config, def_consolidated_config
- def_default_config= "%s/default_config.xml" % config_dir
+ def_default_config = "%s/default_config.xml" % config_dir
def_site_config = "%s/configs/site_config" % config_dir
def_consolidated_config = "%s/%s_config" % (config_dir, service)
global mainloop_usage
- mainloop_usage= """Available commands:
+ mainloop_usage = """Available commands:
Uppercase versions give variables comments, when available
u/U\t\t\tEdit usual variables
w\t\t\tWrite
Typical usage involves: u, [l,] w, r, q
""" % globals()
-def usage ():
- command_usage="%prog [options] [default-xml [site-xml [consolidated-xml]]]"
- init_configuration ()
- command_usage +="""
+
+def usage():
+ command_usage = "%prog [options] [default-xml [site-xml [consolidated-xml]]]"
+ init_configuration()
+ command_usage += """
\t default-xml defaults to %s
\t site-xml defaults to %s
-\t consolidated-xml defaults to %s""" % (def_default_config,def_site_config, def_consolidated_config)
+\t consolidated-xml defaults to %s""" % (def_default_config, def_site_config, def_consolidated_config)
return command_usage
####################
-variable_usage= """Edit Commands :
+variable_usage = """Edit Commands :
#\tShow variable comments
.\tStops prompting, return to mainloop
/\tCleans any site-defined value, reverts to default
"""
####################
-def get_value (config, category_id, variable_id):
- value = config.get (category_id, variable_id)
+
+
+def get_value(config, category_id, variable_id):
+ value = config.get(category_id, variable_id)
return value
-def get_type (config, category_id, variable_id):
- value = config.get (category_id, variable_id)
- #return variable['type']
+
+def get_type(config, category_id, variable_id):
+ value = config.get(category_id, variable_id)
+ # return variable['type']
return str
-def get_current_value (cread, cwrite, category_id, variable_id):
+
+def get_current_value(cread, cwrite, category_id, variable_id):
# the value stored in cwrite, if present, is the one we want
try:
- result=get_value (cwrite,category_id,variable_id)
+ result = get_value(cwrite, category_id, variable_id)
except:
- result=get_value (cread,category_id,variable_id)
+ result = get_value(cread, category_id, variable_id)
return result
# refrain from using plc_config's _sanitize
-def get_varname (config, category_id, variable_id):
- varname = category_id +"_"+ variable_id
+
+
+def get_varname(config, category_id, variable_id):
+ varname = category_id + "_" + variable_id
config.locate_varname(varname)
return varname
# could not avoid using _sanitize here..
-def get_name_comments (config, cid, vid):
+
+
+def get_name_comments(config, cid, vid):
try:
- (category, variable) = config.get (cid, vid)
- (id, name, value, comments) = config._sanitize_variable (cid,variable)
- return (name,comments)
+ (category, variable) = config.get(cid, vid)
+ (id, name, value, comments) = config._sanitize_variable(cid, variable)
+ return (name, comments)
except:
- return (None,[])
+ return (None, [])
+
-def print_name_comments (config, cid, vid):
- (name,comments)=get_name_comments(config,cid,vid)
+def print_name_comments(config, cid, vid):
+ name, comments = get_name_comments(config, cid, vid)
if name:
print "### %s" % name
if comments:
for line in comments:
print "# %s" % line
else:
- print "!!! No comment associated to %s_%s" % (cid,vid)
+ print "!!! No comment associated to %s_%s" % (cid, vid)
####################
-def list_categories (config):
- result=[]
+
+
+def list_categories(config):
+ result = []
for section in config.sections():
result += [section]
return result
-def print_categories (config):
+
+def print_categories(config):
print "Known categories"
for cid in list_categories(config):
print "%s" % (cid.upper())
####################
-def list_category (config, cid):
- result=[]
+
+
+def list_category(config, cid):
+ result = []
for section in config.sections():
if section == cid.lower():
- for (name,value) in config.items(section):
- result += ["%s_%s" %(cid,name)]
+ for (name, value) in config.items(section):
+ result += ["%s_%s" % (cid, name)]
return result
-def print_category (config, cid, show_comments=True):
- cid=cid.lower()
- CID=cid.upper()
- vids=list_category(config,cid)
+
+def print_category(config, cid, show_comments=True):
+ cid = cid.lower()
+ CID = cid.upper()
+ vids = list_category(config, cid)
if (len(vids) == 0):
- print "%s : no such category"%CID
+ print "%s : no such category" % CID
else:
- print "Category %s contains" %(CID)
+ print "Category %s contains" % (CID)
for vid in vids:
print vid.upper()
####################
-def consolidate (default_config, site_config, consolidated_config):
+
+
+def consolidate(default_config, site_config, consolidated_config):
global service
try:
conso = Config(default_config)
- conso.load (site_config)
- conso.save (consolidated_config)
+ conso.load(site_config)
+ conso.save(consolidated_config)
except Exception, inst:
print "Could not consolidate, %s" % (str(inst))
return
- print ("Merged\n\t%s\nand\t%s\ninto\t%s"%(default_config,site_config,
- consolidated_config))
+ print("Merged\n\t%s\nand\t%s\ninto\t%s" % (default_config, site_config,
+ consolidated_config))
-def reload_service ():
+
+def reload_service():
global service
os.system("set -x ; service %s reload" % service)
####################
-def restart_service ():
+
+
+def restart_service():
global service
- print ("==================== Stopping %s" % service)
+ print("==================== Stopping %s" % service)
os.system("service %s stop" % service)
- print ("==================== Starting %s" % service)
+ print("==================== Starting %s" % service)
os.system("service %s start" % service)
####################
-def prompt_variable (cdef, cread, cwrite, category, variable,
- show_comments, support_next=False):
+def prompt_variable(cdef, cread, cwrite, category, variable,
+ show_comments, support_next=False):
+
category_id = category
variable_id = variable
while True:
- default_value = get_value(cdef,category_id,variable_id)
- variable_type = get_type(cdef,category_id,variable_id)
- current_value = get_current_value(cread,cwrite,category_id, variable_id)
- varname = get_varname (cread,category_id, variable_id)
-
- if show_comments :
- print_name_comments (cdef, category_id, variable_id)
- prompt = "== %s : [%s] " % (varname,current_value)
+ default_value = get_value(cdef, category_id, variable_id)
+ variable_type = get_type(cdef, category_id, variable_id)
+ current_value = get_current_value(
+ cread, cwrite, category_id, variable_id)
+ varname = get_varname(cread, category_id, variable_id)
+
+ if show_comments:
+ print_name_comments(cdef, category_id, variable_id)
+ prompt = "== %s : [%s] " % (varname, current_value)
try:
answer = raw_input(prompt).strip()
- except EOFError :
- raise Exception ('BailOut')
+ except EOFError:
+ raise Exception('BailOut')
except KeyboardInterrupt:
print "\n"
- raise Exception ('BailOut')
+ raise Exception('BailOut')
# no change
if (answer == "") or (answer == current_value):
return None
elif (answer == "."):
- raise Exception ('BailOut')
+ raise Exception('BailOut')
elif (answer == "#"):
- print_name_comments(cread,category_id,variable_id)
+ print_name_comments(cread, category_id, variable_id)
elif (answer == "?"):
print variable_usage.strip()
elif (answer == "="):
- print ("%s defaults to %s" %(varname,default_value))
+ print("%s defaults to %s" % (varname, default_value))
# revert to default : remove from cwrite (i.e. site-config)
elif (answer == "/"):
- cwrite.delete(category_id,variable_id)
- print ("%s reverted to %s" %(varname,default_value))
+ cwrite.delete(category_id, variable_id)
+ print("%s reverted to %s" % (varname, default_value))
return
elif (answer == ">"):
if support_next:
- raise Exception ('NextCategory')
+ raise Exception('NextCategory')
else:
print "No support for next category"
else:
else:
print "Not a valid value"
-def prompt_variables_all (cdef, cread, cwrite, show_comments):
+
+def prompt_variables_all(cdef, cread, cwrite, show_comments):
try:
for (category_id, (category, variables)) in cread.variables().iteritems():
- print ("========== Category = %s" % category_id.upper())
+ print("========== Category = %s" % category_id.upper())
for variable in variables.values():
try:
- newvar = prompt_variable (cdef, cread, cwrite, category, variable,
- show_comments, True)
+ newvar = prompt_variable(cdef, cread, cwrite, category, variable,
+ show_comments, True)
except Exception, inst:
- if (str(inst) == 'NextCategory'): break
- else: raise
+ if (str(inst) == 'NextCategory'):
+ break
+ else:
+ raise
except Exception, inst:
- if (str(inst) == 'BailOut'): return
- else: raise
+ if (str(inst) == 'BailOut'):
+ return
+ else:
+ raise
-def prompt_variables_category (cdef, cread, cwrite, cid, show_comments):
- cid=cid.lower()
- CID=cid.upper()
+
+def prompt_variables_category(cdef, cread, cwrite, cid, show_comments):
+ cid = cid.lower()
+ CID = cid.upper()
try:
- print ("========== Category = %s" % CID)
- for vid in list_category(cdef,cid):
- (category,variable) = cdef.locate_varname(vid.upper())
- newvar = prompt_variable (cdef, cread, cwrite, category, variable,
- show_comments, False)
+ print("========== Category = %s" % CID)
+ for vid in list_category(cdef, cid):
+ (category, variable) = cdef.locate_varname(vid.upper())
+ newvar = prompt_variable(cdef, cread, cwrite, category, variable,
+ show_comments, False)
except Exception, inst:
- if (str(inst) == 'BailOut'): return
- else: raise
+ if (str(inst) == 'BailOut'):
+ return
+ else:
+ raise
####################
-def show_variable (cdef, cread, cwrite,
- category, variable,show_value,show_comments):
+
+
+def show_variable(cdef, cread, cwrite,
+ category, variable, show_value, show_comments):
assert category.has_key('id')
assert variable.has_key('id')
- category_id = category ['id']
+ category_id = category['id']
variable_id = variable['id']
- default_value = get_value(cdef,category_id,variable_id)
- current_value = get_current_value(cread,cwrite,category_id,variable_id)
- varname = get_varname (cread,category_id, variable_id)
- if show_comments :
- print_name_comments (cdef, category_id, variable_id)
+ default_value = get_value(cdef, category_id, variable_id)
+ current_value = get_current_value(cread, cwrite, category_id, variable_id)
+ varname = get_varname(cread, category_id, variable_id)
+ if show_comments:
+ print_name_comments(cdef, category_id, variable_id)
if show_value:
- print "%s = %s" % (varname,current_value)
+ print "%s = %s" % (varname, current_value)
else:
print "%s" % (varname)
-def show_variables_all (cdef, cread, cwrite, show_value, show_comments):
+
+def show_variables_all(cdef, cread, cwrite, show_value, show_comments):
for (category_id, (category, variables)) in cread.variables().iteritems():
- print ("========== Category = %s" % category_id.upper())
+ print("========== Category = %s" % category_id.upper())
for variable in variables.values():
- show_variable (cdef, cread, cwrite,
- category, variable,show_value,show_comments)
-
-def show_variables_category (cdef, cread, cwrite, cid, show_value,show_comments):
- cid=cid.lower()
- CID=cid.upper()
- print ("========== Category = %s" % CID)
- for vid in list_category(cdef,cid):
- (category,variable) = cdef.locate_varname(vid.upper())
- show_variable (cdef, cread, cwrite, category, variable,
- show_value,show_comments)
+ show_variable(cdef, cread, cwrite,
+ category, variable, show_value, show_comments)
+
+
+def show_variables_category(cdef, cread, cwrite, cid, show_value, show_comments):
+ cid = cid.lower()
+ CID = cid.upper()
+ print("========== Category = %s" % CID)
+ for vid in list_category(cdef, cid):
+ (category, variable) = cdef.locate_varname(vid.upper())
+ show_variable(cdef, cread, cwrite, category, variable,
+ show_value, show_comments)
####################
-re_mainloop_0arg="^(?P<command>[uUwrRqlLsSeEcvVhH\?])[ \t]*$"
-re_mainloop_1arg="^(?P<command>[sSeEvV])[ \t]+(?P<arg>\w+)$"
-matcher_mainloop_0arg=re.compile(re_mainloop_0arg)
-matcher_mainloop_1arg=re.compile(re_mainloop_1arg)
+re_mainloop_0arg = "^(?P<command>[uUwrRqlLsSeEcvVhH\?])[ \t]*$"
+re_mainloop_1arg = "^(?P<command>[sSeEvV])[ \t]+(?P<arg>\w+)$"
+matcher_mainloop_0arg = re.compile(re_mainloop_0arg)
+matcher_mainloop_1arg = re.compile(re_mainloop_1arg)
-def mainloop (cdef, cread, cwrite, default_config, site_config, consolidated_config):
+
+def mainloop(cdef, cread, cwrite, default_config, site_config, consolidated_config):
global service
while True:
try:
- answer = raw_input("Enter command (u for usual changes, w to save, ? for help) ").strip()
+ answer = raw_input(
+ "Enter command (u for usual changes, w to save, ? for help) ").strip()
except EOFError:
- answer =""
+ answer = ""
except KeyboardInterrupt:
print "\nBye"
sys.exit()
print mainloop_usage
continue
groups_parse = matcher_mainloop_0arg.match(answer)
- command=None
+ command = None
if (groups_parse):
command = groups_parse.group('command')
- arg=None
+ arg = None
else:
groups_parse = matcher_mainloop_1arg.match(answer)
if (groups_parse):
command = groups_parse.group('command')
- arg=groups_parse.group('arg')
+ arg = groups_parse.group('arg')
if not command:
- print ("Unknown command >%s< -- use h for help" % answer)
+ print("Unknown command >%s< -- use h for help" % answer)
continue
- show_comments=command.isupper()
+ show_comments = command.isupper()
- mode='ALL'
+ mode = 'ALL'
if arg:
- mode=None
- arg=arg.lower()
- variables=list_category (cdef,arg)
+ mode = None
+ arg = arg.lower()
+ variables = list_category(cdef, arg)
if len(variables):
# category_id as the category name
# variables as the list of variable names
- mode='CATEGORY'
- category_id=arg
- arg=arg.upper()
- (category,variable)=cdef.locate_varname(arg)
+ mode = 'CATEGORY'
+ category_id = arg
+ arg = arg.upper()
+ (category, variable) = cdef.locate_varname(arg)
if variable:
# category/variable as output by locate_varname
- mode='VARIABLE'
+ mode = 'VARIABLE'
if not mode:
print "%s: no such category or variable" % arg
continue
elif command == "w":
try:
# Confirm that various constraints are met before saving file.
- validate_variables = g_configuration.get('validate_variables',{})
- validated_variables = cwrite.verify(cdef, cread, validate_variables)
- validator = g_configuration.get('validator',noop_validator)
+ validate_variables = g_configuration.get(
+ 'validate_variables', {})
+ validated_variables = cwrite.verify(
+ cdef, cread, validate_variables)
+ validator = g_configuration.get('validator', noop_validator)
validator(validated_variables)
cwrite.save(site_config)
except:
print "Save failed due to a configuration exception:"
print traceback.print_exc()
- print ("Could not save -- fix write access on %s" % site_config)
+ print("Could not save -- fix write access on %s" % site_config)
break
- print ("Wrote %s" % site_config)
+ print("Wrote %s" % site_config)
consolidate(default_config, site_config, consolidated_config)
- print ("You might want to type 'r' (restart %s), 'R' (reload %s) or 'q' (quit)" % \
- (service,service))
+ print("You might want to type 'r' (restart %s), 'R' (reload %s) or 'q' (quit)" %
+ (service, service))
elif command in "uU":
global usual_variables
global flavour_xml_section_hash
try:
for varname in usual_variables:
- (category,variable) = cdef.locate_varname(varname)
+ (category, variable) = cdef.locate_varname(varname)
if not (category is None and variable is None):
- prompt_variable(cdef, cread, cwrite, category, variable, False)
-
- # set the driver variable according to the already set flavour
+ prompt_variable(cdef, cread, cwrite,
+ category, variable, False)
+
+ # set the driver variable according to the already set flavour
generic_flavour = cwrite.items('sfa')[0][1]
for section in cdef.sections():
- if generic_flavour in flavour_xml_section_hash and flavour_xml_section_hash[generic_flavour] == section:
- for item in cdef.items(section):
- category = section
- variable = item[0]
- prompt_variable(cdef, cread, cwrite, category, variable, False)
- break
+ if generic_flavour in flavour_xml_section_hash and flavour_xml_section_hash[generic_flavour] == section:
+ for item in cdef.items(section):
+ category = section
+ variable = item[0]
+ prompt_variable(cdef, cread, cwrite,
+ category, variable, False)
+ break
except Exception, inst:
if (str(inst) != 'BailOut'):
print_categories(cread)
elif command in "eE":
if mode == 'ALL':
- prompt_variables_all(cdef, cread, cwrite,show_comments)
+ prompt_variables_all(cdef, cread, cwrite, show_comments)
elif mode == 'CATEGORY':
- prompt_variables_category(cdef,cread,cwrite,category_id,show_comments)
+ prompt_variables_category(
+ cdef, cread, cwrite, category_id, show_comments)
elif mode == 'VARIABLE':
try:
- prompt_variable (cdef,cread,cwrite,category,variable,
- show_comments,False)
+ prompt_variable(cdef, cread, cwrite, category, variable,
+ show_comments, False)
except Exception, inst:
if str(inst) != 'BailOut':
raise
elif command in "vVsSlL":
- show_value=(command in "sSlL")
- (c1,c2,c3) = (cdef, cread, cwrite)
+ show_value = (command in "sSlL")
+ (c1, c2, c3) = (cdef, cread, cwrite)
if command in "lL":
- (c1,c2,c3) = (cwrite,cwrite,cwrite)
+ (c1, c2, c3) = (cwrite, cwrite, cwrite)
if mode == 'ALL':
- show_variables_all(c1,c2,c3,show_value,show_comments)
+ show_variables_all(c1, c2, c3, show_value, show_comments)
elif mode == 'CATEGORY':
- show_variables_category(c1,c2,c3,category_id,show_value,show_comments)
+ show_variables_category(
+ c1, c2, c3, category_id, show_value, show_comments)
elif mode == 'VARIABLE':
- show_variable (c1,c2,c3,category,variable,show_value,show_comments)
+ show_variable(c1, c2, c3, category, variable,
+ show_value, show_comments)
else:
- print ("Unknown command >%s< -- use h for help" % answer)
+ print("Unknown command >%s< -- use h for help" % answer)
####################
# creates directory for file if not yet existing
-def check_dir (config_file):
- dirname = os.path.dirname (config_file)
- if (not os.path.exists (dirname)):
+def check_dir(config_file):
+ dirname = os.path.dirname(config_file)
+ if (not os.path.exists(dirname)):
try:
- os.makedirs(dirname,0755)
+ os.makedirs(dirname, 0755)
except OSError, e:
- print "Cannot create dir %s due to %s - exiting" % (dirname,e)
+ print "Cannot create dir %s due to %s - exiting" % (dirname, e)
sys.exit(1)
- if (not os.path.exists (dirname)):
+ if (not os.path.exists(dirname)):
print "Cannot create dir %s - exiting" % dirname
sys.exit(1)
else:
print "Created directory %s" % dirname
####################
+
+
def optParserSetup(configuration):
- parser = OptionParser(usage=usage(), version="%prog " + version_tag )
+ parser = OptionParser(usage=usage(), version="%prog " + version_tag)
parser.set_defaults(config_dir=configuration['config_dir'],
service=configuration['service'],
usual_variables=configuration['usual_variables'])
- parser.add_option("","--configdir",dest="config_dir",help="specify configuration directory")
- parser.add_option("","--service",dest="service",help="specify /etc/init.d style service name")
- parser.add_option("","--usual_variable",dest="usual_variables",action="append", help="add a usual variable")
+ parser.add_option("", "--configdir", dest="config_dir",
+ help="specify configuration directory")
+ parser.add_option("", "--service", dest="service",
+ help="specify /etc/init.d style service name")
+ parser.add_option("", "--usual_variable", dest="usual_variables",
+ action="append", help="add a usual variable")
return parser
-def main(command,argv,configuration):
+
+def main(command, argv, configuration):
global g_configuration
- g_configuration=configuration
+ g_configuration = configuration
parser = optParserSetup(configuration)
- (config,args) = parser.parse_args()
- if len(args)>3:
+ (config, args) = parser.parse_args()
+ if len(args) > 3:
parser.error("too many arguments")
- configuration['service']=config.service
- configuration['usual_variables']=config.usual_variables
- configuration['config_dir']=config.config_dir
+ configuration['service'] = config.service
+ configuration['usual_variables'] = config.usual_variables
+ configuration['config_dir'] = config.config_dir
# add in new usual_variables defined on the command line
for usual_variable in config.usual_variables:
if usual_variable not in configuration['usual_variables']:
# intialize configuration
init_configuration()
- (default_config,site_config,consolidated_config) = (def_default_config, def_site_config, def_consolidated_config)
+ default_config, site_config, consolidated_config = \
+ def_default_config, def_site_config, def_consolidated_config
if len(args) >= 1:
- default_config=args[0]
+ default_config = args[0]
if len(args) >= 2:
- site_config=args[1]
+ site_config = args[1]
if len(args) == 3:
- consolidated_config=args[2]
+ consolidated_config = args[2]
- for c in (default_config,site_config,consolidated_config):
- check_dir (c)
+ for c in (default_config, site_config, consolidated_config):
+ check_dir(c)
try:
# the default settings only - read only
cread = Config(default_config)
except:
print traceback.print_exc()
- print ("default config files %s not found, is myplc installed ?" % default_config)
+ print("default config files %s not found, is myplc installed ?" %
+ default_config)
return 1
# local settings only, will be modified & saved
config_filename = "%s/sfa_config" % config.config_dir
- cwrite=Config(config_filename)
+ cwrite = Config(config_filename)
try:
cread.load(site_config)
cwrite.load(default_config)
except:
cwrite = Config()
- mainloop (cdef, cread, cwrite, default_config, site_config, consolidated_config)
- return 0
+ mainloop(cdef, cread, cwrite, default_config,
+ site_config, consolidated_config)
+ return 0
if __name__ == '__main__':
- command=sys.argv[0]
+ command = sys.argv[0]
argv = sys.argv[1:]
- main(command,argv,configuration)
+ main(command, argv, configuration)