9
class AmuletUtils(object):
12
This class provides common utility functions that are used by Amulet
16
def __init__(self, log_level=logging.ERROR):
17
self.log = self.get_logger(level=log_level)
19
def get_logger(self, name="amulet-logger", level=logging.DEBUG):
20
"""Get a logger object that will log to stdout."""
22
logger = log.getLogger(name)
23
fmt = log.Formatter("%(asctime)s %(funcName)s "
24
"%(levelname)s: %(message)s")
26
handler = log.StreamHandler(stream=sys.stdout)
27
handler.setLevel(level)
28
handler.setFormatter(fmt)
30
logger.addHandler(handler)
31
logger.setLevel(level)
35
def valid_ip(self, ip):
36
if re.match(r"^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$", ip):
41
def valid_url(self, url):
44
r'(?:(?:[A-Z0-9](?:[A-Z0-9-]{0,61}[A-Z0-9])?\.)+(?:[A-Z]{2,6}\.?|[A-Z0-9-]{2,}\.?)|' # noqa
46
r'\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})'
55
def validate_services(self, commands):
58
Verify the specified services are running on the corresponding
61
for k, v in commands.iteritems():
63
output, code = k.run(cmd)
65
return "command `{}` returned {}".format(cmd, str(code))
68
def _get_config(self, unit, filename):
69
"""Get a ConfigParser object for parsing a unit's config file."""
70
file_contents = unit.file_contents(filename)
71
config = ConfigParser.ConfigParser()
72
config.readfp(io.StringIO(file_contents))
75
def validate_config_data(self, sentry_unit, config_file, section,
77
"""Validate config file data.
79
Verify that the specified section of the config file contains
80
the expected option key:value pairs.
82
config = self._get_config(sentry_unit, config_file)
84
if section != 'DEFAULT' and not config.has_section(section):
85
return "section [{}] does not exist".format(section)
87
for k in expected.keys():
88
if not config.has_option(section, k):
89
return "section [{}] is missing option {}".format(section, k)
90
if config.get(section, k) != expected[k]:
91
return "section [{}] {}:{} != expected {}:{}".format(
92
section, k, config.get(section, k), k, expected[k])
95
def _validate_dict_data(self, expected, actual):
96
"""Validate dictionary data.
98
Compare expected dictionary data vs actual dictionary data.
99
The values in the 'expected' dictionary can be strings, bools, ints,
100
longs, or can be a function that evaluate a variable and returns a
103
for k, v in expected.iteritems():
105
if (isinstance(v, basestring) or
106
isinstance(v, bool) or
107
isinstance(v, (int, long))):
109
return "{}:{}".format(k, actual[k])
110
elif not v(actual[k]):
111
return "{}:{}".format(k, actual[k])
113
return "key '{}' does not exist".format(k)
116
def validate_relation_data(self, sentry_unit, relation, expected):
117
"""Validate actual relation data based on expected relation data."""
118
actual = sentry_unit.relation(relation[0], relation[1])
119
self.log.debug('actual: {}'.format(repr(actual)))
120
return self._validate_dict_data(expected, actual)
122
def _validate_list_data(self, expected, actual):
123
"""Compare expected list vs actual list data."""
126
return "expected item {} not found in actual list".format(e)
129
def not_null(self, string):
130
if string is not None:
135
def _get_file_mtime(self, sentry_unit, filename):
136
"""Get last modification time of file."""
137
return sentry_unit.file_stat(filename)['mtime']
139
def _get_dir_mtime(self, sentry_unit, directory):
140
"""Get last modification time of directory."""
141
return sentry_unit.directory_stat(directory)['mtime']
143
def _get_proc_start_time(self, sentry_unit, service, pgrep_full=False):
144
"""Get process' start time.
146
Determine start time of the process based on the last modification
147
time of the /proc/pid directory. If pgrep_full is True, the process
148
name is matched against the full command line.
151
cmd = 'pgrep -o -f {}'.format(service)
153
cmd = 'pgrep -o {}'.format(service)
154
proc_dir = '/proc/{}'.format(sentry_unit.run(cmd)[0].strip())
155
return self._get_dir_mtime(sentry_unit, proc_dir)
157
def service_restarted(self, sentry_unit, service, filename,
158
pgrep_full=False, sleep_time=20):
159
"""Check if service was restarted.
161
Compare a service's start time vs a file's last modification time
162
(such as a config file for that service) to determine if the service
165
time.sleep(sleep_time)
166
if (self._get_proc_start_time(sentry_unit, service, pgrep_full) >=
167
self._get_file_mtime(sentry_unit, filename)):
172
def relation_error(self, name, data):
173
return 'unexpected relation data in {} - {}'.format(name, data)
175
def endpoint_error(self, name, data):
176
return 'unexpected endpoint data in {} - {}'.format(name, data)