3 from __future__ import absolute_import, print_function
14 from apiclient import errors as apierror
16 # IOError is the base class for socket.error and friends.
17 # It seems like it hits the sweet spot for operations we want to retry:
18 # it's low-level, but unlikely to catch code bugs.
19 NETWORK_ERRORS = (IOError, ssl.SSLError)
20 ARVADOS_ERRORS = NETWORK_ERRORS + (apierror.Error,)
22 actor_class = pykka.ThreadingActor
24 class NodeManagerConfig(ConfigParser.SafeConfigParser):
25 """Node Manager Configuration class.
27 This a standard Python ConfigParser, with additional helper methods to
28 create objects instantiated with configuration information.
31 LOGGING_NONLEVELS = frozenset(['file'])
33 def __init__(self, *args, **kwargs):
34 # Can't use super() because SafeConfigParser is an old-style class.
35 ConfigParser.SafeConfigParser.__init__(self, *args, **kwargs)
36 for sec_name, settings in {
37 'Arvados': {'insecure': 'no',
39 'Daemon': {'min_nodes': '0',
42 'max_poll_time': '300',
43 'poll_stale_after': '600',
44 'max_total_price': '0',
45 'boot_fail_after': str(sys.maxint),
46 'node_stale_after': str(60 * 60 * 2)},
47 'Logging': {'file': '/dev/stderr',
50 if not self.has_section(sec_name):
51 self.add_section(sec_name)
52 for opt_name, value in settings.iteritems():
53 if not self.has_option(sec_name, opt_name):
54 self.set(sec_name, opt_name, value)
56 def get_section(self, section, transformer=None):
58 for key, value in self.items(section):
59 if transformer is not None:
61 value = transformer(value)
62 except (TypeError, ValueError):
68 return {key: getattr(logging, self.get('Logging', key).upper())
69 for key in self.options('Logging')
70 if key not in self.LOGGING_NONLEVELS}
72 def dispatch_classes(self):
73 mod_name = 'arvnodeman.computenode.dispatch'
74 if self.has_option('Daemon', 'dispatcher'):
75 mod_name = '{}.{}'.format(mod_name,
76 self.get('Daemon', 'dispatcher'))
77 module = importlib.import_module(mod_name)
78 return (module.ComputeNodeSetupActor,
79 module.ComputeNodeShutdownActor,
80 module.ComputeNodeUpdateActor,
81 module.ComputeNodeMonitorActor)
83 def new_arvados_client(self):
84 if self.has_option('Daemon', 'certs_file'):
85 certs_file = self.get('Daemon', 'certs_file')
88 insecure = self.getboolean('Arvados', 'insecure')
89 http = httplib2.Http(timeout=self.getint('Arvados', 'timeout'),
91 disable_ssl_certificate_validation=insecure)
92 return arvados.api(version='v1',
93 host=self.get('Arvados', 'host'),
94 token=self.get('Arvados', 'token'),
98 def new_cloud_client(self):
99 module = importlib.import_module('arvnodeman.computenode.driver.' +
100 self.get('Cloud', 'provider'))
101 auth_kwargs = self.get_section('Cloud Credentials')
102 if 'timeout' in auth_kwargs:
103 auth_kwargs['timeout'] = int(auth_kwargs['timeout'])
104 return module.ComputeNodeDriver(auth_kwargs,
105 self.get_section('Cloud List'),
106 self.get_section('Cloud Create'))
108 def node_sizes(self, all_sizes):
109 """Finds all acceptable NodeSizes for our installation.
111 Returns a list of (NodeSize, kwargs) pairs for each NodeSize object
112 returned by libcloud that matches a size listed in our config file.
116 for sec_name in self.sections():
117 sec_words = sec_name.split(None, 2)
118 if sec_words[0] != 'Size':
120 size_kwargs[sec_words[1]] = self.get_section(sec_name, int)
121 # EC2 node sizes are identified by id. GCE sizes are identified by name.
123 for size in all_sizes:
124 if size.id in size_kwargs:
125 matching_sizes.append((size, size_kwargs[size.id]))
126 elif size.name in size_kwargs:
127 matching_sizes.append((size, size_kwargs[size.name]))
128 return matching_sizes
130 def shutdown_windows(self):
132 for n in self.get('Cloud', 'shutdown_windows').split(',')]