* [PATCH 2/3] Make a standalone client to be able to use global_config.ini
2009-11-17 17:40 [PATCH 1/3] Adding AutoservInstallError to common_lib.error Lucas Meneghel Rodrigues
@ 2009-11-17 17:40 ` Lucas Meneghel Rodrigues
2009-11-17 18:02 ` John Admanski
0 siblings, 1 reply; 8+ messages in thread
From: Lucas Meneghel Rodrigues @ 2009-11-17 17:40 UTC (permalink / raw)
To: autotest; +Cc: kvm, jadmanski, Lucas Meneghel Rodrigues
In order to make it possible the autotest client to use the
global_config.ini configuration files:
* Modified global_config.py to support verifying if the
configuration file is under autotest's root directory,
or the client directory
* Extended the autotest run method to copy over
the configuration files to the client.
Notes for revisors (changes from previous patchset and other stuff):
* Now only the config values under [CLIENT] will be
effectively present on the config file that will
be copied to the client. That config file is generated
using new methods added to global_config.
* No shadow_config or database passwords will ever be
copied to the client. Yay! :)
* common_lib.host_protections was changed accordingly.
* The new config values needed for the patch that follows
are contained on this intermediate patch, so when applied
this patch doesn't break autoserv.
Signed-off-by: Lucas Meneghel Rodrigues <lmr@redhat.com>
---
client/common_lib/global_config.py | 61 +++++++++++++++++++++++++++++----
client/common_lib/host_protections.py | 26 ++++++++------
global_config.ini | 3 ++
server/autotest.py | 33 ++++++++++++++----
4 files changed, 98 insertions(+), 25 deletions(-)
diff --git a/client/common_lib/global_config.py b/client/common_lib/global_config.py
index 2bbeca0..ffa8993 100644
--- a/client/common_lib/global_config.py
+++ b/client/common_lib/global_config.py
@@ -8,12 +8,6 @@ __author__ = 'raphtee@google.com (Travis Miller)'
import os, sys, ConfigParser
from autotest_lib.client.common_lib import error
-dirname = os.path.dirname(sys.modules[__name__].__file__)
-DEFAULT_CONFIG_FILE = os.path.abspath(os.path.join(dirname,
- "../../global_config.ini"))
-DEFAULT_SHADOW_FILE = os.path.abspath(os.path.join(dirname,
- "../../shadow_config.ini"))
-
class ConfigError(error.AutotestError):
pass
@@ -23,12 +17,50 @@ class ConfigValueError(ConfigError):
pass
+
+common_lib_dir = os.path.dirname(sys.modules[__name__].__file__)
+client_dir = os.path.dirname(common_lib_dir)
+root_dir = os.path.dirname(client_dir)
+
+# Check if the config files are at autotest's root dir
+# This will happen if client is executing inside a full autotest tree, or if
+# other entry points are being executed
+global_config_path_root = os.path.join(root_dir, 'global_config.ini')
+shadow_config_path_root = os.path.join(root_dir, 'shadow_config.ini')
+config_in_root = (os.path.exists(global_config_path_root) and
+ os.path.exists(shadow_config_path_root))
+
+# Check if the config files are at autotest's client dir
+# This will happen if a client stand alone execution is happening
+global_config_path_client = os.path.join(client_dir, 'global_config.ini')
+config_in_client = os.path.exists(global_config_path_client)
+
+if config_in_root:
+ DEFAULT_CONFIG_FILE = global_config_path_root
+ DEFAULT_SHADOW_FILE = shadow_config_path_root
+ RUNNING_STAND_ALONE_CLIENT = False
+elif config_in_client:
+ DEFAULT_CONFIG_FILE = global_config_path_client
+ DEFAULT_SHADOW_FILE = None
+ RUNNING_STAND_ALONE_CLIENT = True
+else:
+ raise ConfigError("Could not find configuration files "
+ "needed for this program to function. Please refer to "
+ "http://autotest.kernel.org/wiki/GlobalConfig "
+ "for more info.")
+
+
class global_config(object):
_NO_DEFAULT_SPECIFIED = object()
config = None
config_file = DEFAULT_CONFIG_FILE
shadow_file = DEFAULT_SHADOW_FILE
+ running_stand_alone_client = RUNNING_STAND_ALONE_CLIENT
+
+
+ def check_stand_alone_client_run(self):
+ return self.running_stand_alone_client
def set_config_files(self, config_file=DEFAULT_CONFIG_FILE,
@@ -47,6 +79,21 @@ class global_config(object):
return default
+ def get_session_values(self, section):
+ """
+ Return a config parser object containing a single session of the
+ global configuration, that can be later written to a file object.
+
+ @param session: Session we want to turn into a config parser object.
+ @return: ConfigParser() object containing all the contents of session.
+ """
+ cfgparser = ConfigParser.ConfigParser()
+ cfgparser.add_section(section)
+ for option, value in self.config.items(section):
+ cfgparser.set(section, option, value)
+ return cfgparser
+
+
def get_config_value(self, section, key, type=str,
default=_NO_DEFAULT_SPECIFIED, allow_blank=False):
self._ensure_config_parsed()
@@ -106,7 +153,7 @@ class global_config(object):
# now also read the shadow file if there is one
# this will overwrite anything that is found in the
# other config
- if os.path.exists(self.shadow_file):
+ if self.shadow_file and os.path.exists(self.shadow_file):
shadow_config = ConfigParser.ConfigParser()
shadow_config.read(self.shadow_file)
# now we merge shadow into global
diff --git a/client/common_lib/host_protections.py b/client/common_lib/host_protections.py
index 9457114..7c9e6a0 100644
--- a/client/common_lib/host_protections.py
+++ b/client/common_lib/host_protections.py
@@ -22,19 +22,23 @@ Protection = enum.Enum('No protection', # Repair can do anything to
# this host
)
+running_client = global_config.global_config.check_stand_alone_client_run()
+
try:
_bad_value = object()
- default = Protection.get_value(
- global_config.global_config.get_config_value(
- 'HOSTS', 'default_protection', default=_bad_value))
- if default == _bad_value:
- raise global_config.ConfigError(
- 'No HOSTS.default_protection defined in global_config.ini')
+ default_protection = global_config.global_config.get_config_value(
+ 'HOSTS', 'default_protection', default=_bad_value)
+ if default_protection == _bad_value:
+ if running_client:
+ logging.debug('Client stand alone run detected. '
+ 'host_protection.default will not be set.')
+ else:
+ raise global_config.ConfigError(
+ 'No HOSTS.default_protection defined in global_config.ini')
+ else:
+ default = Protection.get_value(default_protection)
+
except global_config.ConfigError:
- # can happen if no global_config.ini exists at all, but this can actually
- # happen in reasonable cases (e.g. on a standalone clinet) where it's
- # safe to ignore
- logging.debug('No global_config.ini exists so host_protection.default '
- 'will not be defined')
+ raise global_config.ConfigError('No global_config.ini exists, aborting')
choices = Protection.choices()
diff --git a/global_config.ini b/global_config.ini
index 1cd3b4f..ac6baab 100644
--- a/global_config.ini
+++ b/global_config.ini
@@ -28,6 +28,9 @@ parse_failed_repair_default: 0
# Autotest potential install paths
client_autodir_paths: /usr/local/autotest,/home/autotest
+[CLIENT]
+drop_caches: True
+drop_caches_between_iterations: True
[SERVER]
hostname: autotest
diff --git a/server/autotest.py b/server/autotest.py
index 3d307a9..77cef54 100644
--- a/server/autotest.py
+++ b/server/autotest.py
@@ -391,9 +391,15 @@ class BaseAutotest(installable_object.InstallableObject):
cfile += open(tmppath).read()
open(tmppath, "w").write(cfile)
+ # Create and copy configuration file based on the state of the
+ # client configuration
+ client_config_file = self._create_aux_file(host.job)
+ host.send_file(client_config_file, atrun.config_file)
+ os.remove(client_config_file)
+
# Create and copy state file to remote_control_file + '.state'
sysinfo_state = {"__sysinfo": host.job.sysinfo.serialize()}
- state_file = self._create_state_file(host.job, sysinfo_state)
+ state_file = self._create_aux_file(host.job, sysinfo_state)
host.send_file(state_file, atrun.remote_control_file + '.state')
os.remove(state_file)
@@ -407,13 +413,25 @@ class BaseAutotest(installable_object.InstallableObject):
client_disconnect_timeout=client_disconnect_timeout)
- def _create_state_file(self, job, state_dict):
- """ Create a state file from a dictionary. Returns the path of the
- state file. """
+ def _create_aux_file(self, job, state_dict=None):
+ """
+ If state_dict is provided, create a state file from this dictionary. If
+ none provided, it creates a client config file based on the global
+ config session CLIENT.
+
+ @param job: Autotest job instance.
+ @param state_dict: Dictionary containing the job's state.
+ @return: The path of the config file.
+ """
fd, path = tempfile.mkstemp(dir=job.tmpdir)
- state_file = os.fdopen(fd, "w")
- pickle.dump(state_dict, state_file)
- state_file.close()
+ aux_file = os.fdopen(fd, "w")
+ if state_dict:
+ pickle.dump(state_dict, aux_file)
+ else:
+ client_config = global_config.global_config.get_session_values(
+ "CLIENT")
+ client_config.write(aux_file)
+ aux_file.close()
return path
@@ -459,6 +477,7 @@ class _Run(object):
control += '.' + tag
self.manual_control_file = control
self.remote_control_file = control + '.autoserv'
+ self.config_file = os.path.join(self.autodir, 'global_config.ini')
def verify_machine(self):
--
1.6.2.5
^ permalink raw reply related [flat|nested] 8+ messages in thread
* Re: [PATCH 2/3] Make a standalone client to be able to use global_config.ini
2009-11-17 17:40 ` [PATCH 2/3] Make a standalone client to be able to use global_config.ini Lucas Meneghel Rodrigues
@ 2009-11-17 18:02 ` John Admanski
0 siblings, 0 replies; 8+ messages in thread
From: John Admanski @ 2009-11-17 18:02 UTC (permalink / raw)
To: Lucas Meneghel Rodrigues; +Cc: autotest, kvm
On Tue, Nov 17, 2009 at 9:40 AM, Lucas Meneghel Rodrigues
<lmr@redhat.com> wrote:
> In order to make it possible the autotest client to use the
> global_config.ini configuration files:
>
> * Modified global_config.py to support verifying if the
> configuration file is under autotest's root directory,
> or the client directory
> * Extended the autotest run method to copy over
> the configuration files to the client.
>
> Notes for revisors (changes from previous patchset and other stuff):
>
> * Now only the config values under [CLIENT] will be
> effectively present on the config file that will
> be copied to the client. That config file is generated
> using new methods added to global_config.
> * No shadow_config or database passwords will ever be
> copied to the client. Yay! :)
> * common_lib.host_protections was changed accordingly.
> * The new config values needed for the patch that follows
> are contained on this intermediate patch, so when applied
> this patch doesn't break autoserv.
>
> Signed-off-by: Lucas Meneghel Rodrigues <lmr@redhat.com>
> ---
> client/common_lib/global_config.py | 61 +++++++++++++++++++++++++++++----
> client/common_lib/host_protections.py | 26 ++++++++------
> global_config.ini | 3 ++
> server/autotest.py | 33 ++++++++++++++----
> 4 files changed, 98 insertions(+), 25 deletions(-)
>
> diff --git a/client/common_lib/global_config.py b/client/common_lib/global_config.py
> index 2bbeca0..ffa8993 100644
> --- a/client/common_lib/global_config.py
> +++ b/client/common_lib/global_config.py
> @@ -8,12 +8,6 @@ __author__ = 'raphtee@google.com (Travis Miller)'
> import os, sys, ConfigParser
> from autotest_lib.client.common_lib import error
>
> -dirname = os.path.dirname(sys.modules[__name__].__file__)
> -DEFAULT_CONFIG_FILE = os.path.abspath(os.path.join(dirname,
> - "../../global_config.ini"))
> -DEFAULT_SHADOW_FILE = os.path.abspath(os.path.join(dirname,
> - "../../shadow_config.ini"))
> -
>
> class ConfigError(error.AutotestError):
> pass
> @@ -23,12 +17,50 @@ class ConfigValueError(ConfigError):
> pass
>
>
> +
> +common_lib_dir = os.path.dirname(sys.modules[__name__].__file__)
> +client_dir = os.path.dirname(common_lib_dir)
> +root_dir = os.path.dirname(client_dir)
> +
> +# Check if the config files are at autotest's root dir
> +# This will happen if client is executing inside a full autotest tree, or if
> +# other entry points are being executed
> +global_config_path_root = os.path.join(root_dir, 'global_config.ini')
> +shadow_config_path_root = os.path.join(root_dir, 'shadow_config.ini')
> +config_in_root = (os.path.exists(global_config_path_root) and
> + os.path.exists(shadow_config_path_root))
> +
> +# Check if the config files are at autotest's client dir
> +# This will happen if a client stand alone execution is happening
> +global_config_path_client = os.path.join(client_dir, 'global_config.ini')
> +config_in_client = os.path.exists(global_config_path_client)
> +
> +if config_in_root:
> + DEFAULT_CONFIG_FILE = global_config_path_root
> + DEFAULT_SHADOW_FILE = shadow_config_path_root
> + RUNNING_STAND_ALONE_CLIENT = False
> +elif config_in_client:
> + DEFAULT_CONFIG_FILE = global_config_path_client
> + DEFAULT_SHADOW_FILE = None
> + RUNNING_STAND_ALONE_CLIENT = True
> +else:
> + raise ConfigError("Could not find configuration files "
> + "needed for this program to function. Please refer to "
> + "http://autotest.kernel.org/wiki/GlobalConfig "
> + "for more info.")
> +
> +
> class global_config(object):
> _NO_DEFAULT_SPECIFIED = object()
>
> config = None
> config_file = DEFAULT_CONFIG_FILE
> shadow_file = DEFAULT_SHADOW_FILE
> + running_stand_alone_client = RUNNING_STAND_ALONE_CLIENT
> +
> +
> + def check_stand_alone_client_run(self):
> + return self.running_stand_alone_client
I don't know if it's really necessary to have a method wrapped around
this, instead of just accessing the attribute directly. I've actually
been trying to re-factor existing code that does this to get rid of
getters.
>
>
> def set_config_files(self, config_file=DEFAULT_CONFIG_FILE,
> @@ -47,6 +79,21 @@ class global_config(object):
> return default
>
>
> + def get_session_values(self, section):
> + """
> + Return a config parser object containing a single session of the
> + global configuration, that can be later written to a file object.
> +
> + @param session: Session we want to turn into a config parser object.
> + @return: ConfigParser() object containing all the contents of session.
> + """
> + cfgparser = ConfigParser.ConfigParser()
> + cfgparser.add_section(section)
> + for option, value in self.config.items(section):
> + cfgparser.set(section, option, value)
> + return cfgparser
> +
> +
The method name, parameter and the docstring aren't consistent
(session vs section). For the record, I favor section over session.
> def get_config_value(self, section, key, type=str,
> default=_NO_DEFAULT_SPECIFIED, allow_blank=False):
> self._ensure_config_parsed()
> @@ -106,7 +153,7 @@ class global_config(object):
> # now also read the shadow file if there is one
> # this will overwrite anything that is found in the
> # other config
> - if os.path.exists(self.shadow_file):
> + if self.shadow_file and os.path.exists(self.shadow_file):
> shadow_config = ConfigParser.ConfigParser()
> shadow_config.read(self.shadow_file)
> # now we merge shadow into global
> diff --git a/client/common_lib/host_protections.py b/client/common_lib/host_protections.py
> index 9457114..7c9e6a0 100644
> --- a/client/common_lib/host_protections.py
> +++ b/client/common_lib/host_protections.py
> @@ -22,19 +22,23 @@ Protection = enum.Enum('No protection', # Repair can do anything to
> # this host
> )
>
> +running_client = global_config.global_config.check_stand_alone_client_run()
> +
> try:
> _bad_value = object()
> - default = Protection.get_value(
> - global_config.global_config.get_config_value(
> - 'HOSTS', 'default_protection', default=_bad_value))
> - if default == _bad_value:
> - raise global_config.ConfigError(
> - 'No HOSTS.default_protection defined in global_config.ini')
> + default_protection = global_config.global_config.get_config_value(
> + 'HOSTS', 'default_protection', default=_bad_value)
> + if default_protection == _bad_value:
> + if running_client:
> + logging.debug('Client stand alone run detected. '
> + 'host_protection.default will not be set.')
> + else:
> + raise global_config.ConfigError(
> + 'No HOSTS.default_protection defined in global_config.ini')
> + else:
> + default = Protection.get_value(default_protection)
> +
> except global_config.ConfigError:
> - # can happen if no global_config.ini exists at all, but this can actually
> - # happen in reasonable cases (e.g. on a standalone clinet) where it's
> - # safe to ignore
> - logging.debug('No global_config.ini exists so host_protection.default '
> - 'will not be defined')
> + raise global_config.ConfigError('No global_config.ini exists, aborting')
>
> choices = Protection.choices()
> diff --git a/global_config.ini b/global_config.ini
> index 1cd3b4f..ac6baab 100644
> --- a/global_config.ini
> +++ b/global_config.ini
> @@ -28,6 +28,9 @@ parse_failed_repair_default: 0
> # Autotest potential install paths
> client_autodir_paths: /usr/local/autotest,/home/autotest
>
> +[CLIENT]
> +drop_caches: True
> +drop_caches_between_iterations: True
>
> [SERVER]
> hostname: autotest
> diff --git a/server/autotest.py b/server/autotest.py
> index 3d307a9..77cef54 100644
> --- a/server/autotest.py
> +++ b/server/autotest.py
> @@ -391,9 +391,15 @@ class BaseAutotest(installable_object.InstallableObject):
> cfile += open(tmppath).read()
> open(tmppath, "w").write(cfile)
>
> + # Create and copy configuration file based on the state of the
> + # client configuration
> + client_config_file = self._create_aux_file(host.job)
> + host.send_file(client_config_file, atrun.config_file)
> + os.remove(client_config_file)
> +
> # Create and copy state file to remote_control_file + '.state'
> sysinfo_state = {"__sysinfo": host.job.sysinfo.serialize()}
> - state_file = self._create_state_file(host.job, sysinfo_state)
> + state_file = self._create_aux_file(host.job, sysinfo_state)
> host.send_file(state_file, atrun.remote_control_file + '.state')
> os.remove(state_file)
>
> @@ -407,13 +413,25 @@ class BaseAutotest(installable_object.InstallableObject):
> client_disconnect_timeout=client_disconnect_timeout)
>
>
> - def _create_state_file(self, job, state_dict):
> - """ Create a state file from a dictionary. Returns the path of the
> - state file. """
> + def _create_aux_file(self, job, state_dict=None):
> + """
> + If state_dict is provided, create a state file from this dictionary. If
> + none provided, it creates a client config file based on the global
> + config session CLIENT.
This seems kind of...magical somehow. I understand why you're doing it
this way to reuse the file creation code, but I'd actually take a
different approach using higher order functions. Instead of having it
take a parameter and then use an if-else to decide what kind of
initialization to do, pass in a function. Then make create_state_file
and create_config_file wrappers that call it with the appropriate
function (either doing the pickle.dump, or the client config
creation).
I know that seems kind of convoluted, but I really don't like
"generic" methods that do very different things depending on which
parameters you pass in. If we want a generic "create a temp file and
write some stuff into it" function, we should make a truly generic
one.
> +
> + @param job: Autotest job instance.
> + @param state_dict: Dictionary containing the job's state.
> + @return: The path of the config file.
> + """
> fd, path = tempfile.mkstemp(dir=job.tmpdir)
> - state_file = os.fdopen(fd, "w")
> - pickle.dump(state_dict, state_file)
> - state_file.close()
> + aux_file = os.fdopen(fd, "w")
> + if state_dict:
> + pickle.dump(state_dict, aux_file)
> + else:
> + client_config = global_config.global_config.get_session_values(
> + "CLIENT")
> + client_config.write(aux_file)
> + aux_file.close()
> return path
>
>
> @@ -459,6 +477,7 @@ class _Run(object):
> control += '.' + tag
> self.manual_control_file = control
> self.remote_control_file = control + '.autoserv'
> + self.config_file = os.path.join(self.autodir, 'global_config.ini')
>
>
> def verify_machine(self):
> --
> 1.6.2.5
>
>
^ permalink raw reply [flat|nested] 8+ messages in thread
* [PATCH 2/3] Make a standalone client to be able to use global_config.ini
2009-11-18 0:56 [PATCH 1/3] Adding AutoservInstallError to common_lib.error Lucas Meneghel Rodrigues
@ 2009-11-18 0:56 ` Lucas Meneghel Rodrigues
0 siblings, 0 replies; 8+ messages in thread
From: Lucas Meneghel Rodrigues @ 2009-11-18 0:56 UTC (permalink / raw)
To: autotest; +Cc: kvm, jadmanski, Lucas Meneghel Rodrigues
In order to make it possible the autotest client to use the
global_config.ini configuration files:
* Modified global_config.py to support verifying if the
configuration file is under autotest's root directory,
or the client directory
* Extended the autotest run method to copy over
the configuration files to the client.
Notes for revisors (changes from previous patchset and other stuff):
* The ugly create_aux_file was turned into a generic function that
shows consistent behavior regardless of the parameters passed in,
then 2 wrapper functions, create_state_file and create_config_file
are being used, both calling create_aux_file.
* Fixed spelling errors on the get_section_values docstring, as
well as the function name, making them all consistent with 'section'.
---
client/common_lib/global_config.py | 61 +++++++++++++++++++++++++++++----
client/common_lib/host_protections.py | 26 ++++++++------
global_config.ini | 3 ++
server/autotest.py | 51 +++++++++++++++++++++++++---
4 files changed, 118 insertions(+), 23 deletions(-)
diff --git a/client/common_lib/global_config.py b/client/common_lib/global_config.py
index 2bbeca0..6c801ba 100644
--- a/client/common_lib/global_config.py
+++ b/client/common_lib/global_config.py
@@ -8,12 +8,6 @@ __author__ = 'raphtee@google.com (Travis Miller)'
import os, sys, ConfigParser
from autotest_lib.client.common_lib import error
-dirname = os.path.dirname(sys.modules[__name__].__file__)
-DEFAULT_CONFIG_FILE = os.path.abspath(os.path.join(dirname,
- "../../global_config.ini"))
-DEFAULT_SHADOW_FILE = os.path.abspath(os.path.join(dirname,
- "../../shadow_config.ini"))
-
class ConfigError(error.AutotestError):
pass
@@ -23,12 +17,50 @@ class ConfigValueError(ConfigError):
pass
+
+common_lib_dir = os.path.dirname(sys.modules[__name__].__file__)
+client_dir = os.path.dirname(common_lib_dir)
+root_dir = os.path.dirname(client_dir)
+
+# Check if the config files are at autotest's root dir
+# This will happen if client is executing inside a full autotest tree, or if
+# other entry points are being executed
+global_config_path_root = os.path.join(root_dir, 'global_config.ini')
+shadow_config_path_root = os.path.join(root_dir, 'shadow_config.ini')
+config_in_root = (os.path.exists(global_config_path_root) and
+ os.path.exists(shadow_config_path_root))
+
+# Check if the config files are at autotest's client dir
+# This will happen if a client stand alone execution is happening
+global_config_path_client = os.path.join(client_dir, 'global_config.ini')
+config_in_client = os.path.exists(global_config_path_client)
+
+if config_in_root:
+ DEFAULT_CONFIG_FILE = global_config_path_root
+ DEFAULT_SHADOW_FILE = shadow_config_path_root
+ RUNNING_STAND_ALONE_CLIENT = False
+elif config_in_client:
+ DEFAULT_CONFIG_FILE = global_config_path_client
+ DEFAULT_SHADOW_FILE = None
+ RUNNING_STAND_ALONE_CLIENT = True
+else:
+ raise ConfigError("Could not find configuration files "
+ "needed for this program to function. Please refer to "
+ "http://autotest.kernel.org/wiki/GlobalConfig "
+ "for more info.")
+
+
class global_config(object):
_NO_DEFAULT_SPECIFIED = object()
config = None
config_file = DEFAULT_CONFIG_FILE
shadow_file = DEFAULT_SHADOW_FILE
+ running_stand_alone_client = RUNNING_STAND_ALONE_CLIENT
+
+
+ def check_stand_alone_client_run(self):
+ return self.running_stand_alone_client
def set_config_files(self, config_file=DEFAULT_CONFIG_FILE,
@@ -47,6 +79,21 @@ class global_config(object):
return default
+ def get_section_values(self, section):
+ """
+ Return a config parser object containing a single session of the
+ global configuration, that can be later written to a file object.
+
+ @param section: Section we want to turn into a config parser object.
+ @return: ConfigParser() object containing all the contents of session.
+ """
+ cfgparser = ConfigParser.ConfigParser()
+ cfgparser.add_section(section)
+ for option, value in self.config.items(section):
+ cfgparser.set(section, option, value)
+ return cfgparser
+
+
def get_config_value(self, section, key, type=str,
default=_NO_DEFAULT_SPECIFIED, allow_blank=False):
self._ensure_config_parsed()
@@ -106,7 +153,7 @@ class global_config(object):
# now also read the shadow file if there is one
# this will overwrite anything that is found in the
# other config
- if os.path.exists(self.shadow_file):
+ if self.shadow_file and os.path.exists(self.shadow_file):
shadow_config = ConfigParser.ConfigParser()
shadow_config.read(self.shadow_file)
# now we merge shadow into global
diff --git a/client/common_lib/host_protections.py b/client/common_lib/host_protections.py
index 9457114..7c9e6a0 100644
--- a/client/common_lib/host_protections.py
+++ b/client/common_lib/host_protections.py
@@ -22,19 +22,23 @@ Protection = enum.Enum('No protection', # Repair can do anything to
# this host
)
+running_client = global_config.global_config.check_stand_alone_client_run()
+
try:
_bad_value = object()
- default = Protection.get_value(
- global_config.global_config.get_config_value(
- 'HOSTS', 'default_protection', default=_bad_value))
- if default == _bad_value:
- raise global_config.ConfigError(
- 'No HOSTS.default_protection defined in global_config.ini')
+ default_protection = global_config.global_config.get_config_value(
+ 'HOSTS', 'default_protection', default=_bad_value)
+ if default_protection == _bad_value:
+ if running_client:
+ logging.debug('Client stand alone run detected. '
+ 'host_protection.default will not be set.')
+ else:
+ raise global_config.ConfigError(
+ 'No HOSTS.default_protection defined in global_config.ini')
+ else:
+ default = Protection.get_value(default_protection)
+
except global_config.ConfigError:
- # can happen if no global_config.ini exists at all, but this can actually
- # happen in reasonable cases (e.g. on a standalone clinet) where it's
- # safe to ignore
- logging.debug('No global_config.ini exists so host_protection.default '
- 'will not be defined')
+ raise global_config.ConfigError('No global_config.ini exists, aborting')
choices = Protection.choices()
diff --git a/global_config.ini b/global_config.ini
index 1cd3b4f..ac6baab 100644
--- a/global_config.ini
+++ b/global_config.ini
@@ -28,6 +28,9 @@ parse_failed_repair_default: 0
# Autotest potential install paths
client_autodir_paths: /usr/local/autotest,/home/autotest
+[CLIENT]
+drop_caches: True
+drop_caches_between_iterations: True
[SERVER]
hostname: autotest
diff --git a/server/autotest.py b/server/autotest.py
index 3d307a9..a915196 100644
--- a/server/autotest.py
+++ b/server/autotest.py
@@ -391,6 +391,12 @@ class BaseAutotest(installable_object.InstallableObject):
cfile += open(tmppath).read()
open(tmppath, "w").write(cfile)
+ # Create and copy configuration file based on the state of the
+ # client configuration
+ client_config_file = self._create_client_config_file(host.job)
+ host.send_file(client_config_file, atrun.config_file)
+ os.remove(client_config_file)
+
# Create and copy state file to remote_control_file + '.state'
sysinfo_state = {"__sysinfo": host.job.sysinfo.serialize()}
state_file = self._create_state_file(host.job, sysinfo_state)
@@ -408,12 +414,46 @@ class BaseAutotest(installable_object.InstallableObject):
def _create_state_file(self, job, state_dict):
- """ Create a state file from a dictionary. Returns the path of the
- state file. """
+ """
+ Create a temporary file with the state described by state_dict.
+
+ @param job: Autotest job.
+ @param state_dict: Dictionary containing the state we want to write to
+ the temporary file
+ @return: Path of the temporary file generated.
+ """
+ return self._create_aux_file(job, pickle.dump, state_dict)
+
+
+ def _create_client_config_file(self, job):
+ """
+ Create a temporary file with the [CLIENT] section configuration values
+ taken from the server global_config.ini.
+
+ @param job: Autotest job.
+ @return: Path of the temporary file generated.
+ """
+ client_config = global_config.global_config.get_section_values("CLIENT")
+ return self._create_aux_file(job, client_config.write)
+
+
+ def _create_aux_file(self, job, func, *args):
+ """
+ Creates a temporary file and writes content to it according to a content
+ creation function.
+
+ @param job: Autotest job instance.
+ @param func: Function that will be used to write content to the
+ temporary file.
+ @param *args: List of parameters that func takes.
+ @return: Path to the temporary file that was created.
+ """
fd, path = tempfile.mkstemp(dir=job.tmpdir)
- state_file = os.fdopen(fd, "w")
- pickle.dump(state_dict, state_file)
- state_file.close()
+ aux_file = os.fdopen(fd, "w")
+ args = list(args)
+ args.append(aux_file)
+ func(*args)
+ aux_file.close()
return path
@@ -459,6 +499,7 @@ class _Run(object):
control += '.' + tag
self.manual_control_file = control
self.remote_control_file = control + '.autoserv'
+ self.config_file = os.path.join(self.autodir, 'global_config.ini')
def verify_machine(self):
--
1.6.2.5
^ permalink raw reply related [flat|nested] 8+ messages in thread
* [PATCH 1/3] Adding AutoservInstallError to common_lib.error
@ 2009-11-18 1:04 Lucas Meneghel Rodrigues
2009-11-18 1:04 ` [PATCH 2/3] Make a standalone client to be able to use global_config.ini Lucas Meneghel Rodrigues
0 siblings, 1 reply; 8+ messages in thread
From: Lucas Meneghel Rodrigues @ 2009-11-18 1:04 UTC (permalink / raw)
To: autotest; +Cc: kvm, jadmanski, Lucas Meneghel Rodrigues
server/autotest.py was referencing it but it didn't exist.
This patch creates the error class.
Signed-off-by: Lucas Meneghel Rodrigues <lmr@redhat.com>
---
client/common_lib/error.py | 5 +++++
1 files changed, 5 insertions(+), 0 deletions(-)
diff --git a/client/common_lib/error.py b/client/common_lib/error.py
index 08401ce..9e0002d 100644
--- a/client/common_lib/error.py
+++ b/client/common_lib/error.py
@@ -185,6 +185,11 @@ class AutoservError(Exception):
pass
+class AutoservInstallError(Exception):
+ """Autoserv failed in installing autotest on a client machine"""
+ pass
+
+
class AutoservSSHTimeout(AutoservError):
"""SSH experienced a connection timeout"""
pass
--
1.6.2.5
^ permalink raw reply related [flat|nested] 8+ messages in thread
* [PATCH 2/3] Make a standalone client to be able to use global_config.ini
2009-11-18 1:04 [PATCH 1/3] Adding AutoservInstallError to common_lib.error Lucas Meneghel Rodrigues
@ 2009-11-18 1:04 ` Lucas Meneghel Rodrigues
2009-11-18 15:40 ` John Admanski
0 siblings, 1 reply; 8+ messages in thread
From: Lucas Meneghel Rodrigues @ 2009-11-18 1:04 UTC (permalink / raw)
To: autotest; +Cc: kvm, jadmanski, Lucas Meneghel Rodrigues
In order to make it possible the autotest client to use the
global_config.ini configuration files:
* Modified global_config.py to support verifying if the
configuration file is under autotest's root directory,
or the client directory
* Extended the autotest run method to copy over
the configuration files to the client.
Notes for revisors (changes from previous patchset and other stuff):
* The ugly create_aux_file was turned into a generic function that
shows consistent behavior regardless of the parameters passed in,
then 2 wrapper functions, create_state_file and create_config_file
are being used, both calling create_aux_file.
* Fixed spelling errors on the get_section_values docstring, as
well as the function name, making them all consistent with 'section'.
---
client/common_lib/global_config.py | 61 +++++++++++++++++++++++++++++----
client/common_lib/host_protections.py | 26 ++++++++------
global_config.ini | 3 ++
server/autotest.py | 51 +++++++++++++++++++++++++---
4 files changed, 118 insertions(+), 23 deletions(-)
diff --git a/client/common_lib/global_config.py b/client/common_lib/global_config.py
index 2bbeca0..6c801ba 100644
--- a/client/common_lib/global_config.py
+++ b/client/common_lib/global_config.py
@@ -8,12 +8,6 @@ __author__ = 'raphtee@google.com (Travis Miller)'
import os, sys, ConfigParser
from autotest_lib.client.common_lib import error
-dirname = os.path.dirname(sys.modules[__name__].__file__)
-DEFAULT_CONFIG_FILE = os.path.abspath(os.path.join(dirname,
- "../../global_config.ini"))
-DEFAULT_SHADOW_FILE = os.path.abspath(os.path.join(dirname,
- "../../shadow_config.ini"))
-
class ConfigError(error.AutotestError):
pass
@@ -23,12 +17,50 @@ class ConfigValueError(ConfigError):
pass
+
+common_lib_dir = os.path.dirname(sys.modules[__name__].__file__)
+client_dir = os.path.dirname(common_lib_dir)
+root_dir = os.path.dirname(client_dir)
+
+# Check if the config files are at autotest's root dir
+# This will happen if client is executing inside a full autotest tree, or if
+# other entry points are being executed
+global_config_path_root = os.path.join(root_dir, 'global_config.ini')
+shadow_config_path_root = os.path.join(root_dir, 'shadow_config.ini')
+config_in_root = (os.path.exists(global_config_path_root) and
+ os.path.exists(shadow_config_path_root))
+
+# Check if the config files are at autotest's client dir
+# This will happen if a client stand alone execution is happening
+global_config_path_client = os.path.join(client_dir, 'global_config.ini')
+config_in_client = os.path.exists(global_config_path_client)
+
+if config_in_root:
+ DEFAULT_CONFIG_FILE = global_config_path_root
+ DEFAULT_SHADOW_FILE = shadow_config_path_root
+ RUNNING_STAND_ALONE_CLIENT = False
+elif config_in_client:
+ DEFAULT_CONFIG_FILE = global_config_path_client
+ DEFAULT_SHADOW_FILE = None
+ RUNNING_STAND_ALONE_CLIENT = True
+else:
+ raise ConfigError("Could not find configuration files "
+ "needed for this program to function. Please refer to "
+ "http://autotest.kernel.org/wiki/GlobalConfig "
+ "for more info.")
+
+
class global_config(object):
_NO_DEFAULT_SPECIFIED = object()
config = None
config_file = DEFAULT_CONFIG_FILE
shadow_file = DEFAULT_SHADOW_FILE
+ running_stand_alone_client = RUNNING_STAND_ALONE_CLIENT
+
+
+ def check_stand_alone_client_run(self):
+ return self.running_stand_alone_client
def set_config_files(self, config_file=DEFAULT_CONFIG_FILE,
@@ -47,6 +79,21 @@ class global_config(object):
return default
+ def get_section_values(self, section):
+ """
+ Return a config parser object containing a single session of the
+ global configuration, that can be later written to a file object.
+
+ @param section: Section we want to turn into a config parser object.
+ @return: ConfigParser() object containing all the contents of session.
+ """
+ cfgparser = ConfigParser.ConfigParser()
+ cfgparser.add_section(section)
+ for option, value in self.config.items(section):
+ cfgparser.set(section, option, value)
+ return cfgparser
+
+
def get_config_value(self, section, key, type=str,
default=_NO_DEFAULT_SPECIFIED, allow_blank=False):
self._ensure_config_parsed()
@@ -106,7 +153,7 @@ class global_config(object):
# now also read the shadow file if there is one
# this will overwrite anything that is found in the
# other config
- if os.path.exists(self.shadow_file):
+ if self.shadow_file and os.path.exists(self.shadow_file):
shadow_config = ConfigParser.ConfigParser()
shadow_config.read(self.shadow_file)
# now we merge shadow into global
diff --git a/client/common_lib/host_protections.py b/client/common_lib/host_protections.py
index 9457114..7c9e6a0 100644
--- a/client/common_lib/host_protections.py
+++ b/client/common_lib/host_protections.py
@@ -22,19 +22,23 @@ Protection = enum.Enum('No protection', # Repair can do anything to
# this host
)
+running_client = global_config.global_config.check_stand_alone_client_run()
+
try:
_bad_value = object()
- default = Protection.get_value(
- global_config.global_config.get_config_value(
- 'HOSTS', 'default_protection', default=_bad_value))
- if default == _bad_value:
- raise global_config.ConfigError(
- 'No HOSTS.default_protection defined in global_config.ini')
+ default_protection = global_config.global_config.get_config_value(
+ 'HOSTS', 'default_protection', default=_bad_value)
+ if default_protection == _bad_value:
+ if running_client:
+ logging.debug('Client stand alone run detected. '
+ 'host_protection.default will not be set.')
+ else:
+ raise global_config.ConfigError(
+ 'No HOSTS.default_protection defined in global_config.ini')
+ else:
+ default = Protection.get_value(default_protection)
+
except global_config.ConfigError:
- # can happen if no global_config.ini exists at all, but this can actually
- # happen in reasonable cases (e.g. on a standalone clinet) where it's
- # safe to ignore
- logging.debug('No global_config.ini exists so host_protection.default '
- 'will not be defined')
+ raise global_config.ConfigError('No global_config.ini exists, aborting')
choices = Protection.choices()
diff --git a/global_config.ini b/global_config.ini
index 1cd3b4f..ac6baab 100644
--- a/global_config.ini
+++ b/global_config.ini
@@ -28,6 +28,9 @@ parse_failed_repair_default: 0
# Autotest potential install paths
client_autodir_paths: /usr/local/autotest,/home/autotest
+[CLIENT]
+drop_caches: True
+drop_caches_between_iterations: True
[SERVER]
hostname: autotest
diff --git a/server/autotest.py b/server/autotest.py
index 3d307a9..a915196 100644
--- a/server/autotest.py
+++ b/server/autotest.py
@@ -391,6 +391,12 @@ class BaseAutotest(installable_object.InstallableObject):
cfile += open(tmppath).read()
open(tmppath, "w").write(cfile)
+ # Create and copy configuration file based on the state of the
+ # client configuration
+ client_config_file = self._create_client_config_file(host.job)
+ host.send_file(client_config_file, atrun.config_file)
+ os.remove(client_config_file)
+
# Create and copy state file to remote_control_file + '.state'
sysinfo_state = {"__sysinfo": host.job.sysinfo.serialize()}
state_file = self._create_state_file(host.job, sysinfo_state)
@@ -408,12 +414,46 @@ class BaseAutotest(installable_object.InstallableObject):
def _create_state_file(self, job, state_dict):
- """ Create a state file from a dictionary. Returns the path of the
- state file. """
+ """
+ Create a temporary file with the state described by state_dict.
+
+ @param job: Autotest job.
+ @param state_dict: Dictionary containing the state we want to write to
+ the temporary file
+ @return: Path of the temporary file generated.
+ """
+ return self._create_aux_file(job, pickle.dump, state_dict)
+
+
+ def _create_client_config_file(self, job):
+ """
+ Create a temporary file with the [CLIENT] section configuration values
+ taken from the server global_config.ini.
+
+ @param job: Autotest job.
+ @return: Path of the temporary file generated.
+ """
+ client_config = global_config.global_config.get_section_values("CLIENT")
+ return self._create_aux_file(job, client_config.write)
+
+
+ def _create_aux_file(self, job, func, *args):
+ """
+ Creates a temporary file and writes content to it according to a content
+ creation function.
+
+ @param job: Autotest job instance.
+ @param func: Function that will be used to write content to the
+ temporary file.
+ @param *args: List of parameters that func takes.
+ @return: Path to the temporary file that was created.
+ """
fd, path = tempfile.mkstemp(dir=job.tmpdir)
- state_file = os.fdopen(fd, "w")
- pickle.dump(state_dict, state_file)
- state_file.close()
+ aux_file = os.fdopen(fd, "w")
+ args = list(args)
+ args.append(aux_file)
+ func(*args)
+ aux_file.close()
return path
@@ -459,6 +499,7 @@ class _Run(object):
control += '.' + tag
self.manual_control_file = control
self.remote_control_file = control + '.autoserv'
+ self.config_file = os.path.join(self.autodir, 'global_config.ini')
def verify_machine(self):
--
1.6.2.5
^ permalink raw reply related [flat|nested] 8+ messages in thread
* Re: [PATCH 2/3] Make a standalone client to be able to use global_config.ini
2009-11-18 1:04 ` [PATCH 2/3] Make a standalone client to be able to use global_config.ini Lucas Meneghel Rodrigues
@ 2009-11-18 15:40 ` John Admanski
0 siblings, 0 replies; 8+ messages in thread
From: John Admanski @ 2009-11-18 15:40 UTC (permalink / raw)
To: Lucas Meneghel Rodrigues; +Cc: autotest, kvm
Almost there. Just a couple of minor things.
On Tue, Nov 17, 2009 at 5:04 PM, Lucas Meneghel Rodrigues
<lmr@redhat.com> wrote:
> In order to make it possible the autotest client to use the
> global_config.ini configuration files:
>
> * Modified global_config.py to support verifying if the
> configuration file is under autotest's root directory,
> or the client directory
> * Extended the autotest run method to copy over
> the configuration files to the client.
>
> Notes for revisors (changes from previous patchset and other stuff):
>
> * The ugly create_aux_file was turned into a generic function that
> shows consistent behavior regardless of the parameters passed in,
> then 2 wrapper functions, create_state_file and create_config_file
> are being used, both calling create_aux_file.
> * Fixed spelling errors on the get_section_values docstring, as
> well as the function name, making them all consistent with 'section'.
> ---
> client/common_lib/global_config.py | 61 +++++++++++++++++++++++++++++----
> client/common_lib/host_protections.py | 26 ++++++++------
> global_config.ini | 3 ++
> server/autotest.py | 51 +++++++++++++++++++++++++---
> 4 files changed, 118 insertions(+), 23 deletions(-)
>
> diff --git a/client/common_lib/global_config.py b/client/common_lib/global_config.py
> index 2bbeca0..6c801ba 100644
> --- a/client/common_lib/global_config.py
> +++ b/client/common_lib/global_config.py
> @@ -8,12 +8,6 @@ __author__ = 'raphtee@google.com (Travis Miller)'
> import os, sys, ConfigParser
> from autotest_lib.client.common_lib import error
>
> -dirname = os.path.dirname(sys.modules[__name__].__file__)
> -DEFAULT_CONFIG_FILE = os.path.abspath(os.path.join(dirname,
> - "../../global_config.ini"))
> -DEFAULT_SHADOW_FILE = os.path.abspath(os.path.join(dirname,
> - "../../shadow_config.ini"))
> -
>
> class ConfigError(error.AutotestError):
> pass
> @@ -23,12 +17,50 @@ class ConfigValueError(ConfigError):
> pass
>
>
> +
> +common_lib_dir = os.path.dirname(sys.modules[__name__].__file__)
> +client_dir = os.path.dirname(common_lib_dir)
> +root_dir = os.path.dirname(client_dir)
> +
> +# Check if the config files are at autotest's root dir
> +# This will happen if client is executing inside a full autotest tree, or if
> +# other entry points are being executed
> +global_config_path_root = os.path.join(root_dir, 'global_config.ini')
> +shadow_config_path_root = os.path.join(root_dir, 'shadow_config.ini')
> +config_in_root = (os.path.exists(global_config_path_root) and
> + os.path.exists(shadow_config_path_root))
> +
> +# Check if the config files are at autotest's client dir
> +# This will happen if a client stand alone execution is happening
> +global_config_path_client = os.path.join(client_dir, 'global_config.ini')
> +config_in_client = os.path.exists(global_config_path_client)
> +
> +if config_in_root:
> + DEFAULT_CONFIG_FILE = global_config_path_root
> + DEFAULT_SHADOW_FILE = shadow_config_path_root
> + RUNNING_STAND_ALONE_CLIENT = False
> +elif config_in_client:
> + DEFAULT_CONFIG_FILE = global_config_path_client
> + DEFAULT_SHADOW_FILE = None
> + RUNNING_STAND_ALONE_CLIENT = True
> +else:
> + raise ConfigError("Could not find configuration files "
> + "needed for this program to function. Please refer to "
> + "http://autotest.kernel.org/wiki/GlobalConfig "
> + "for more info.")
> +
> +
> class global_config(object):
> _NO_DEFAULT_SPECIFIED = object()
>
> config = None
> config_file = DEFAULT_CONFIG_FILE
> shadow_file = DEFAULT_SHADOW_FILE
> + running_stand_alone_client = RUNNING_STAND_ALONE_CLIENT
> +
> +
> + def check_stand_alone_client_run(self):
> + return self.running_stand_alone_client
>
>
> def set_config_files(self, config_file=DEFAULT_CONFIG_FILE,
> @@ -47,6 +79,21 @@ class global_config(object):
> return default
>
>
> + def get_section_values(self, section):
> + """
> + Return a config parser object containing a single session of the
> + global configuration, that can be later written to a file object.
> +
> + @param section: Section we want to turn into a config parser object.
> + @return: ConfigParser() object containing all the contents of session.
> + """
This docstring still mentions "session" a couple times.
> + cfgparser = ConfigParser.ConfigParser()
> + cfgparser.add_section(section)
> + for option, value in self.config.items(section):
> + cfgparser.set(section, option, value)
> + return cfgparser
> +
> +
> def get_config_value(self, section, key, type=str,
> default=_NO_DEFAULT_SPECIFIED, allow_blank=False):
> self._ensure_config_parsed()
> @@ -106,7 +153,7 @@ class global_config(object):
> # now also read the shadow file if there is one
> # this will overwrite anything that is found in the
> # other config
> - if os.path.exists(self.shadow_file):
> + if self.shadow_file and os.path.exists(self.shadow_file):
> shadow_config = ConfigParser.ConfigParser()
> shadow_config.read(self.shadow_file)
> # now we merge shadow into global
> diff --git a/client/common_lib/host_protections.py b/client/common_lib/host_protections.py
> index 9457114..7c9e6a0 100644
> --- a/client/common_lib/host_protections.py
> +++ b/client/common_lib/host_protections.py
> @@ -22,19 +22,23 @@ Protection = enum.Enum('No protection', # Repair can do anything to
> # this host
> )
>
> +running_client = global_config.global_config.check_stand_alone_client_run()
> +
> try:
> _bad_value = object()
> - default = Protection.get_value(
> - global_config.global_config.get_config_value(
> - 'HOSTS', 'default_protection', default=_bad_value))
> - if default == _bad_value:
> - raise global_config.ConfigError(
> - 'No HOSTS.default_protection defined in global_config.ini')
> + default_protection = global_config.global_config.get_config_value(
> + 'HOSTS', 'default_protection', default=_bad_value)
> + if default_protection == _bad_value:
> + if running_client:
> + logging.debug('Client stand alone run detected. '
> + 'host_protection.default will not be set.')
> + else:
> + raise global_config.ConfigError(
> + 'No HOSTS.default_protection defined in global_config.ini')
> + else:
> + default = Protection.get_value(default_protection)
> +
> except global_config.ConfigError:
> - # can happen if no global_config.ini exists at all, but this can actually
> - # happen in reasonable cases (e.g. on a standalone clinet) where it's
> - # safe to ignore
> - logging.debug('No global_config.ini exists so host_protection.default '
> - 'will not be defined')
> + raise global_config.ConfigError('No global_config.ini exists, aborting')
>
> choices = Protection.choices()
> diff --git a/global_config.ini b/global_config.ini
> index 1cd3b4f..ac6baab 100644
> --- a/global_config.ini
> +++ b/global_config.ini
> @@ -28,6 +28,9 @@ parse_failed_repair_default: 0
> # Autotest potential install paths
> client_autodir_paths: /usr/local/autotest,/home/autotest
>
> +[CLIENT]
> +drop_caches: True
> +drop_caches_between_iterations: True
>
> [SERVER]
> hostname: autotest
> diff --git a/server/autotest.py b/server/autotest.py
> index 3d307a9..a915196 100644
> --- a/server/autotest.py
> +++ b/server/autotest.py
> @@ -391,6 +391,12 @@ class BaseAutotest(installable_object.InstallableObject):
> cfile += open(tmppath).read()
> open(tmppath, "w").write(cfile)
>
> + # Create and copy configuration file based on the state of the
> + # client configuration
> + client_config_file = self._create_client_config_file(host.job)
> + host.send_file(client_config_file, atrun.config_file)
> + os.remove(client_config_file)
> +
> # Create and copy state file to remote_control_file + '.state'
> sysinfo_state = {"__sysinfo": host.job.sysinfo.serialize()}
> state_file = self._create_state_file(host.job, sysinfo_state)
> @@ -408,12 +414,46 @@ class BaseAutotest(installable_object.InstallableObject):
>
>
> def _create_state_file(self, job, state_dict):
> - """ Create a state file from a dictionary. Returns the path of the
> - state file. """
> + """
> + Create a temporary file with the state described by state_dict.
> +
> + @param job: Autotest job.
> + @param state_dict: Dictionary containing the state we want to write to
> + the temporary file
> + @return: Path of the temporary file generated.
> + """
> + return self._create_aux_file(job, pickle.dump, state_dict)
> +
> +
> + def _create_client_config_file(self, job):
> + """
> + Create a temporary file with the [CLIENT] section configuration values
> + taken from the server global_config.ini.
> +
> + @param job: Autotest job.
> + @return: Path of the temporary file generated.
> + """
> + client_config = global_config.global_config.get_section_values("CLIENT")
> + return self._create_aux_file(job, client_config.write)
> +
> +
> + def _create_aux_file(self, job, func, *args):
> + """
> + Creates a temporary file and writes content to it according to a content
> + creation function.
> +
> + @param job: Autotest job instance.
> + @param func: Function that will be used to write content to the
> + temporary file.
> + @param *args: List of parameters that func takes.
> + @return: Path to the temporary file that was created.
> + """
> fd, path = tempfile.mkstemp(dir=job.tmpdir)
> - state_file = os.fdopen(fd, "w")
> - pickle.dump(state_dict, state_file)
> - state_file.close()
> + aux_file = os.fdopen(fd, "w")
> + args = list(args)
> + args.append(aux_file)
> + func(*args)
I think this is a little weird (we pass in args, but append the file
to it as the last argument)? I think the docstring should mention the
fact that the temporary file object is added on to the args. It might
also be a good idea to use a different name when you convert args to a
list and add aux_file to it, as when I first read this code it didn't
immediately jump out at me that func(*args) wasn't using the same args
that were passed in.
> + aux_file.close()
> return path
>
>
> @@ -459,6 +499,7 @@ class _Run(object):
> control += '.' + tag
> self.manual_control_file = control
> self.remote_control_file = control + '.autoserv'
> + self.config_file = os.path.join(self.autodir, 'global_config.ini')
>
>
> def verify_machine(self):
> --
> 1.6.2.5
>
>
^ permalink raw reply [flat|nested] 8+ messages in thread
* [PATCH 2/3] Make a standalone client to be able to use global_config.ini
@ 2009-11-18 16:09 Lucas Meneghel Rodrigues
2009-11-18 16:23 ` John Admanski
0 siblings, 1 reply; 8+ messages in thread
From: Lucas Meneghel Rodrigues @ 2009-11-18 16:09 UTC (permalink / raw)
To: autotest; +Cc: kvm, jadmanski, Lucas Meneghel Rodrigues
In order to make it possible the autotest client to use the
global_config.ini configuration files:
* Modified global_config.py to support verifying if the
configuration file is under autotest's root directory,
or the client directory
* Extended the autotest run method to copy over
the configuration files to the client.
Notes for revisors (changes from previous patchset and other stuff):
* Latest comments from John adressed
Signed-off-by: Lucas Meneghel Rodrigues <lmr@redhat.com>
---
client/common_lib/global_config.py | 61 +++++++++++++++++++++++++++++----
client/common_lib/host_protections.py | 26 ++++++++------
global_config.ini | 3 ++
server/autotest.py | 52 +++++++++++++++++++++++++---
4 files changed, 119 insertions(+), 23 deletions(-)
diff --git a/client/common_lib/global_config.py b/client/common_lib/global_config.py
index 2bbeca0..04ab7ff 100644
--- a/client/common_lib/global_config.py
+++ b/client/common_lib/global_config.py
@@ -8,12 +8,6 @@ __author__ = 'raphtee@google.com (Travis Miller)'
import os, sys, ConfigParser
from autotest_lib.client.common_lib import error
-dirname = os.path.dirname(sys.modules[__name__].__file__)
-DEFAULT_CONFIG_FILE = os.path.abspath(os.path.join(dirname,
- "../../global_config.ini"))
-DEFAULT_SHADOW_FILE = os.path.abspath(os.path.join(dirname,
- "../../shadow_config.ini"))
-
class ConfigError(error.AutotestError):
pass
@@ -23,12 +17,50 @@ class ConfigValueError(ConfigError):
pass
+
+common_lib_dir = os.path.dirname(sys.modules[__name__].__file__)
+client_dir = os.path.dirname(common_lib_dir)
+root_dir = os.path.dirname(client_dir)
+
+# Check if the config files are at autotest's root dir
+# This will happen if client is executing inside a full autotest tree, or if
+# other entry points are being executed
+global_config_path_root = os.path.join(root_dir, 'global_config.ini')
+shadow_config_path_root = os.path.join(root_dir, 'shadow_config.ini')
+config_in_root = (os.path.exists(global_config_path_root) and
+ os.path.exists(shadow_config_path_root))
+
+# Check if the config files are at autotest's client dir
+# This will happen if a client stand alone execution is happening
+global_config_path_client = os.path.join(client_dir, 'global_config.ini')
+config_in_client = os.path.exists(global_config_path_client)
+
+if config_in_root:
+ DEFAULT_CONFIG_FILE = global_config_path_root
+ DEFAULT_SHADOW_FILE = shadow_config_path_root
+ RUNNING_STAND_ALONE_CLIENT = False
+elif config_in_client:
+ DEFAULT_CONFIG_FILE = global_config_path_client
+ DEFAULT_SHADOW_FILE = None
+ RUNNING_STAND_ALONE_CLIENT = True
+else:
+ raise ConfigError("Could not find configuration files "
+ "needed for this program to function. Please refer to "
+ "http://autotest.kernel.org/wiki/GlobalConfig "
+ "for more info.")
+
+
class global_config(object):
_NO_DEFAULT_SPECIFIED = object()
config = None
config_file = DEFAULT_CONFIG_FILE
shadow_file = DEFAULT_SHADOW_FILE
+ running_stand_alone_client = RUNNING_STAND_ALONE_CLIENT
+
+
+ def check_stand_alone_client_run(self):
+ return self.running_stand_alone_client
def set_config_files(self, config_file=DEFAULT_CONFIG_FILE,
@@ -47,6 +79,21 @@ class global_config(object):
return default
+ def get_section_values(self, section):
+ """
+ Return a config parser object containing a single section of the
+ global configuration, that can be later written to a file object.
+
+ @param section: Section we want to turn into a config parser object.
+ @return: ConfigParser() object containing all the contents of section.
+ """
+ cfgparser = ConfigParser.ConfigParser()
+ cfgparser.add_section(section)
+ for option, value in self.config.items(section):
+ cfgparser.set(section, option, value)
+ return cfgparser
+
+
def get_config_value(self, section, key, type=str,
default=_NO_DEFAULT_SPECIFIED, allow_blank=False):
self._ensure_config_parsed()
@@ -106,7 +153,7 @@ class global_config(object):
# now also read the shadow file if there is one
# this will overwrite anything that is found in the
# other config
- if os.path.exists(self.shadow_file):
+ if self.shadow_file and os.path.exists(self.shadow_file):
shadow_config = ConfigParser.ConfigParser()
shadow_config.read(self.shadow_file)
# now we merge shadow into global
diff --git a/client/common_lib/host_protections.py b/client/common_lib/host_protections.py
index 9457114..7c9e6a0 100644
--- a/client/common_lib/host_protections.py
+++ b/client/common_lib/host_protections.py
@@ -22,19 +22,23 @@ Protection = enum.Enum('No protection', # Repair can do anything to
# this host
)
+running_client = global_config.global_config.check_stand_alone_client_run()
+
try:
_bad_value = object()
- default = Protection.get_value(
- global_config.global_config.get_config_value(
- 'HOSTS', 'default_protection', default=_bad_value))
- if default == _bad_value:
- raise global_config.ConfigError(
- 'No HOSTS.default_protection defined in global_config.ini')
+ default_protection = global_config.global_config.get_config_value(
+ 'HOSTS', 'default_protection', default=_bad_value)
+ if default_protection == _bad_value:
+ if running_client:
+ logging.debug('Client stand alone run detected. '
+ 'host_protection.default will not be set.')
+ else:
+ raise global_config.ConfigError(
+ 'No HOSTS.default_protection defined in global_config.ini')
+ else:
+ default = Protection.get_value(default_protection)
+
except global_config.ConfigError:
- # can happen if no global_config.ini exists at all, but this can actually
- # happen in reasonable cases (e.g. on a standalone clinet) where it's
- # safe to ignore
- logging.debug('No global_config.ini exists so host_protection.default '
- 'will not be defined')
+ raise global_config.ConfigError('No global_config.ini exists, aborting')
choices = Protection.choices()
diff --git a/global_config.ini b/global_config.ini
index 1cd3b4f..ac6baab 100644
--- a/global_config.ini
+++ b/global_config.ini
@@ -28,6 +28,9 @@ parse_failed_repair_default: 0
# Autotest potential install paths
client_autodir_paths: /usr/local/autotest,/home/autotest
+[CLIENT]
+drop_caches: True
+drop_caches_between_iterations: True
[SERVER]
hostname: autotest
diff --git a/server/autotest.py b/server/autotest.py
index 3d307a9..9270437 100644
--- a/server/autotest.py
+++ b/server/autotest.py
@@ -391,6 +391,12 @@ class BaseAutotest(installable_object.InstallableObject):
cfile += open(tmppath).read()
open(tmppath, "w").write(cfile)
+ # Create and copy configuration file based on the state of the
+ # client configuration
+ client_config_file = self._create_client_config_file(host.job)
+ host.send_file(client_config_file, atrun.config_file)
+ os.remove(client_config_file)
+
# Create and copy state file to remote_control_file + '.state'
sysinfo_state = {"__sysinfo": host.job.sysinfo.serialize()}
state_file = self._create_state_file(host.job, sysinfo_state)
@@ -408,12 +414,47 @@ class BaseAutotest(installable_object.InstallableObject):
def _create_state_file(self, job, state_dict):
- """ Create a state file from a dictionary. Returns the path of the
- state file. """
+ """
+ Create a temporary file with the state described by state_dict.
+
+ @param job: Autotest job.
+ @param state_dict: Dictionary containing the state we want to write to
+ the temporary file
+ @return: Path of the temporary file generated.
+ """
+ return self._create_aux_file(job, pickle.dump, state_dict)
+
+
+ def _create_client_config_file(self, job):
+ """
+ Create a temporary file with the [CLIENT] section configuration values
+ taken from the server global_config.ini.
+
+ @param job: Autotest job.
+ @return: Path of the temporary file generated.
+ """
+ client_config = global_config.global_config.get_section_values("CLIENT")
+ return self._create_aux_file(job, client_config.write)
+
+
+ def _create_aux_file(self, job, func, *args):
+ """
+ Creates a temporary file and writes content to it according to a content
+ creation function. The file object is appended to *args, which is then
+ passed to the content creation function
+
+ @param job: Autotest job instance.
+ @param func: Function that will be used to write content to the
+ temporary file.
+ @param *args: List of parameters that func takes.
+ @return: Path to the temporary file that was created.
+ """
fd, path = tempfile.mkstemp(dir=job.tmpdir)
- state_file = os.fdopen(fd, "w")
- pickle.dump(state_dict, state_file)
- state_file.close()
+ aux_file = os.fdopen(fd, "w")
+ list_args = list(args)
+ list_args.append(aux_file)
+ func(*list_args)
+ aux_file.close()
return path
@@ -459,6 +500,7 @@ class _Run(object):
control += '.' + tag
self.manual_control_file = control
self.remote_control_file = control + '.autoserv'
+ self.config_file = os.path.join(self.autodir, 'global_config.ini')
def verify_machine(self):
--
1.6.2.5
^ permalink raw reply related [flat|nested] 8+ messages in thread
* Re: [PATCH 2/3] Make a standalone client to be able to use global_config.ini
2009-11-18 16:09 Lucas Meneghel Rodrigues
@ 2009-11-18 16:23 ` John Admanski
0 siblings, 0 replies; 8+ messages in thread
From: John Admanski @ 2009-11-18 16:23 UTC (permalink / raw)
To: Lucas Meneghel Rodrigues; +Cc: autotest, kvm
Looks good.
On Wed, Nov 18, 2009 at 8:09 AM, Lucas Meneghel Rodrigues
<lmr@redhat.com> wrote:
> In order to make it possible the autotest client to use the
> global_config.ini configuration files:
>
> * Modified global_config.py to support verifying if the
> configuration file is under autotest's root directory,
> or the client directory
> * Extended the autotest run method to copy over
> the configuration files to the client.
>
> Notes for revisors (changes from previous patchset and other stuff):
>
> * Latest comments from John adressed
>
> Signed-off-by: Lucas Meneghel Rodrigues <lmr@redhat.com>
> ---
> client/common_lib/global_config.py | 61 +++++++++++++++++++++++++++++----
> client/common_lib/host_protections.py | 26 ++++++++------
> global_config.ini | 3 ++
> server/autotest.py | 52 +++++++++++++++++++++++++---
> 4 files changed, 119 insertions(+), 23 deletions(-)
>
> diff --git a/client/common_lib/global_config.py b/client/common_lib/global_config.py
> index 2bbeca0..04ab7ff 100644
> --- a/client/common_lib/global_config.py
> +++ b/client/common_lib/global_config.py
> @@ -8,12 +8,6 @@ __author__ = 'raphtee@google.com (Travis Miller)'
> import os, sys, ConfigParser
> from autotest_lib.client.common_lib import error
>
> -dirname = os.path.dirname(sys.modules[__name__].__file__)
> -DEFAULT_CONFIG_FILE = os.path.abspath(os.path.join(dirname,
> - "../../global_config.ini"))
> -DEFAULT_SHADOW_FILE = os.path.abspath(os.path.join(dirname,
> - "../../shadow_config.ini"))
> -
>
> class ConfigError(error.AutotestError):
> pass
> @@ -23,12 +17,50 @@ class ConfigValueError(ConfigError):
> pass
>
>
> +
> +common_lib_dir = os.path.dirname(sys.modules[__name__].__file__)
> +client_dir = os.path.dirname(common_lib_dir)
> +root_dir = os.path.dirname(client_dir)
> +
> +# Check if the config files are at autotest's root dir
> +# This will happen if client is executing inside a full autotest tree, or if
> +# other entry points are being executed
> +global_config_path_root = os.path.join(root_dir, 'global_config.ini')
> +shadow_config_path_root = os.path.join(root_dir, 'shadow_config.ini')
> +config_in_root = (os.path.exists(global_config_path_root) and
> + os.path.exists(shadow_config_path_root))
> +
> +# Check if the config files are at autotest's client dir
> +# This will happen if a client stand alone execution is happening
> +global_config_path_client = os.path.join(client_dir, 'global_config.ini')
> +config_in_client = os.path.exists(global_config_path_client)
> +
> +if config_in_root:
> + DEFAULT_CONFIG_FILE = global_config_path_root
> + DEFAULT_SHADOW_FILE = shadow_config_path_root
> + RUNNING_STAND_ALONE_CLIENT = False
> +elif config_in_client:
> + DEFAULT_CONFIG_FILE = global_config_path_client
> + DEFAULT_SHADOW_FILE = None
> + RUNNING_STAND_ALONE_CLIENT = True
> +else:
> + raise ConfigError("Could not find configuration files "
> + "needed for this program to function. Please refer to "
> + "http://autotest.kernel.org/wiki/GlobalConfig "
> + "for more info.")
> +
> +
> class global_config(object):
> _NO_DEFAULT_SPECIFIED = object()
>
> config = None
> config_file = DEFAULT_CONFIG_FILE
> shadow_file = DEFAULT_SHADOW_FILE
> + running_stand_alone_client = RUNNING_STAND_ALONE_CLIENT
> +
> +
> + def check_stand_alone_client_run(self):
> + return self.running_stand_alone_client
>
>
> def set_config_files(self, config_file=DEFAULT_CONFIG_FILE,
> @@ -47,6 +79,21 @@ class global_config(object):
> return default
>
>
> + def get_section_values(self, section):
> + """
> + Return a config parser object containing a single section of the
> + global configuration, that can be later written to a file object.
> +
> + @param section: Section we want to turn into a config parser object.
> + @return: ConfigParser() object containing all the contents of section.
> + """
> + cfgparser = ConfigParser.ConfigParser()
> + cfgparser.add_section(section)
> + for option, value in self.config.items(section):
> + cfgparser.set(section, option, value)
> + return cfgparser
> +
> +
> def get_config_value(self, section, key, type=str,
> default=_NO_DEFAULT_SPECIFIED, allow_blank=False):
> self._ensure_config_parsed()
> @@ -106,7 +153,7 @@ class global_config(object):
> # now also read the shadow file if there is one
> # this will overwrite anything that is found in the
> # other config
> - if os.path.exists(self.shadow_file):
> + if self.shadow_file and os.path.exists(self.shadow_file):
> shadow_config = ConfigParser.ConfigParser()
> shadow_config.read(self.shadow_file)
> # now we merge shadow into global
> diff --git a/client/common_lib/host_protections.py b/client/common_lib/host_protections.py
> index 9457114..7c9e6a0 100644
> --- a/client/common_lib/host_protections.py
> +++ b/client/common_lib/host_protections.py
> @@ -22,19 +22,23 @@ Protection = enum.Enum('No protection', # Repair can do anything to
> # this host
> )
>
> +running_client = global_config.global_config.check_stand_alone_client_run()
> +
> try:
> _bad_value = object()
> - default = Protection.get_value(
> - global_config.global_config.get_config_value(
> - 'HOSTS', 'default_protection', default=_bad_value))
> - if default == _bad_value:
> - raise global_config.ConfigError(
> - 'No HOSTS.default_protection defined in global_config.ini')
> + default_protection = global_config.global_config.get_config_value(
> + 'HOSTS', 'default_protection', default=_bad_value)
> + if default_protection == _bad_value:
> + if running_client:
> + logging.debug('Client stand alone run detected. '
> + 'host_protection.default will not be set.')
> + else:
> + raise global_config.ConfigError(
> + 'No HOSTS.default_protection defined in global_config.ini')
> + else:
> + default = Protection.get_value(default_protection)
> +
> except global_config.ConfigError:
> - # can happen if no global_config.ini exists at all, but this can actually
> - # happen in reasonable cases (e.g. on a standalone clinet) where it's
> - # safe to ignore
> - logging.debug('No global_config.ini exists so host_protection.default '
> - 'will not be defined')
> + raise global_config.ConfigError('No global_config.ini exists, aborting')
>
> choices = Protection.choices()
> diff --git a/global_config.ini b/global_config.ini
> index 1cd3b4f..ac6baab 100644
> --- a/global_config.ini
> +++ b/global_config.ini
> @@ -28,6 +28,9 @@ parse_failed_repair_default: 0
> # Autotest potential install paths
> client_autodir_paths: /usr/local/autotest,/home/autotest
>
> +[CLIENT]
> +drop_caches: True
> +drop_caches_between_iterations: True
>
> [SERVER]
> hostname: autotest
> diff --git a/server/autotest.py b/server/autotest.py
> index 3d307a9..9270437 100644
> --- a/server/autotest.py
> +++ b/server/autotest.py
> @@ -391,6 +391,12 @@ class BaseAutotest(installable_object.InstallableObject):
> cfile += open(tmppath).read()
> open(tmppath, "w").write(cfile)
>
> + # Create and copy configuration file based on the state of the
> + # client configuration
> + client_config_file = self._create_client_config_file(host.job)
> + host.send_file(client_config_file, atrun.config_file)
> + os.remove(client_config_file)
> +
> # Create and copy state file to remote_control_file + '.state'
> sysinfo_state = {"__sysinfo": host.job.sysinfo.serialize()}
> state_file = self._create_state_file(host.job, sysinfo_state)
> @@ -408,12 +414,47 @@ class BaseAutotest(installable_object.InstallableObject):
>
>
> def _create_state_file(self, job, state_dict):
> - """ Create a state file from a dictionary. Returns the path of the
> - state file. """
> + """
> + Create a temporary file with the state described by state_dict.
> +
> + @param job: Autotest job.
> + @param state_dict: Dictionary containing the state we want to write to
> + the temporary file
> + @return: Path of the temporary file generated.
> + """
> + return self._create_aux_file(job, pickle.dump, state_dict)
> +
> +
> + def _create_client_config_file(self, job):
> + """
> + Create a temporary file with the [CLIENT] section configuration values
> + taken from the server global_config.ini.
> +
> + @param job: Autotest job.
> + @return: Path of the temporary file generated.
> + """
> + client_config = global_config.global_config.get_section_values("CLIENT")
> + return self._create_aux_file(job, client_config.write)
> +
> +
> + def _create_aux_file(self, job, func, *args):
> + """
> + Creates a temporary file and writes content to it according to a content
> + creation function. The file object is appended to *args, which is then
> + passed to the content creation function
> +
> + @param job: Autotest job instance.
> + @param func: Function that will be used to write content to the
> + temporary file.
> + @param *args: List of parameters that func takes.
> + @return: Path to the temporary file that was created.
> + """
> fd, path = tempfile.mkstemp(dir=job.tmpdir)
> - state_file = os.fdopen(fd, "w")
> - pickle.dump(state_dict, state_file)
> - state_file.close()
> + aux_file = os.fdopen(fd, "w")
> + list_args = list(args)
> + list_args.append(aux_file)
> + func(*list_args)
> + aux_file.close()
> return path
>
>
> @@ -459,6 +500,7 @@ class _Run(object):
> control += '.' + tag
> self.manual_control_file = control
> self.remote_control_file = control + '.autoserv'
> + self.config_file = os.path.join(self.autodir, 'global_config.ini')
>
>
> def verify_machine(self):
> --
> 1.6.2.5
>
>
^ permalink raw reply [flat|nested] 8+ messages in thread
end of thread, other threads:[~2009-11-18 16:23 UTC | newest]
Thread overview: 8+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2009-11-18 1:04 [PATCH 1/3] Adding AutoservInstallError to common_lib.error Lucas Meneghel Rodrigues
2009-11-18 1:04 ` [PATCH 2/3] Make a standalone client to be able to use global_config.ini Lucas Meneghel Rodrigues
2009-11-18 15:40 ` John Admanski
-- strict thread matches above, loose matches on Subject: below --
2009-11-18 16:09 Lucas Meneghel Rodrigues
2009-11-18 16:23 ` John Admanski
2009-11-18 0:56 [PATCH 1/3] Adding AutoservInstallError to common_lib.error Lucas Meneghel Rodrigues
2009-11-18 0:56 ` [PATCH 2/3] Make a standalone client to be able to use global_config.ini Lucas Meneghel Rodrigues
2009-11-17 17:40 [PATCH 1/3] Adding AutoservInstallError to common_lib.error Lucas Meneghel Rodrigues
2009-11-17 17:40 ` [PATCH 2/3] Make a standalone client to be able to use global_config.ini Lucas Meneghel Rodrigues
2009-11-17 18:02 ` John Admanski
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox