Log watchdog exception refs #9303
[arvados.git] / services / nodemanager / arvnodeman / config.py
index 754b9319f0797d864ab0c9eb50107054485e6eed..b54461c47d81b9f87bc1096da254cdf60e3f8aa9 100644 (file)
@@ -5,22 +5,22 @@ from __future__ import absolute_import, print_function
 import ConfigParser
 import importlib
 import logging
-import ssl
+import sys
 
 import arvados
 import httplib2
-import libcloud.common.types as cloud_types
 import pykka
 from apiclient import errors as apierror
 
-# IOError is the base class for socket.error and friends.
+from .baseactor import BaseNodeManagerActor
+
+# IOError is the base class for socket.error, ssl.SSLError, and friends.
 # It seems like it hits the sweet spot for operations we want to retry:
 # it's low-level, but unlikely to catch code bugs.
-NETWORK_ERRORS = (IOError, ssl.SSLError)
+NETWORK_ERRORS = (IOError,)
 ARVADOS_ERRORS = NETWORK_ERRORS + (apierror.Error,)
-CLOUD_ERRORS = NETWORK_ERRORS + (cloud_types.LibcloudError,)
 
-actor_class = pykka.ThreadingActor
+actor_class = BaseNodeManagerActor
 
 class NodeManagerConfig(ConfigParser.SafeConfigParser):
     """Node Manager Configuration class.
@@ -42,7 +42,10 @@ class NodeManagerConfig(ConfigParser.SafeConfigParser):
                        'poll_time': '60',
                        'max_poll_time': '300',
                        'poll_stale_after': '600',
-                       'node_stale_after': str(60 * 60 * 2)},
+                       'max_total_price': '0',
+                       'boot_fail_after': str(sys.maxint),
+                       'node_stale_after': str(60 * 60 * 2),
+                       'watchdog': '600'},
             'Logging': {'file': '/dev/stderr',
                         'level': 'WARNING'},
         }.iteritems():
@@ -68,6 +71,17 @@ class NodeManagerConfig(ConfigParser.SafeConfigParser):
                 for key in self.options('Logging')
                 if key not in self.LOGGING_NONLEVELS}
 
+    def dispatch_classes(self):
+        mod_name = 'arvnodeman.computenode.dispatch'
+        if self.has_option('Daemon', 'dispatcher'):
+            mod_name = '{}.{}'.format(mod_name,
+                                      self.get('Daemon', 'dispatcher'))
+        module = importlib.import_module(mod_name)
+        return (module.ComputeNodeSetupActor,
+                module.ComputeNodeShutdownActor,
+                module.ComputeNodeUpdateActor,
+                module.ComputeNodeMonitorActor)
+
     def new_arvados_client(self):
         if self.has_option('Daemon', 'certs_file'):
             certs_file = self.get('Daemon', 'certs_file')
@@ -77,15 +91,14 @@ class NodeManagerConfig(ConfigParser.SafeConfigParser):
         http = httplib2.Http(timeout=self.getint('Arvados', 'timeout'),
                              ca_certs=certs_file,
                              disable_ssl_certificate_validation=insecure)
-        return arvados.api('v1',
-                           cache=False,  # Don't reuse an existing client.
+        return arvados.api(version='v1',
                            host=self.get('Arvados', 'host'),
                            token=self.get('Arvados', 'token'),
                            insecure=insecure,
                            http=http)
 
     def new_cloud_client(self):
-        module = importlib.import_module('arvnodeman.computenode.' +
+        module = importlib.import_module('arvnodeman.computenode.driver.' +
                                          self.get('Cloud', 'provider'))
         auth_kwargs = self.get_section('Cloud Credentials')
         if 'timeout' in auth_kwargs:
@@ -95,14 +108,29 @@ class NodeManagerConfig(ConfigParser.SafeConfigParser):
                                         self.get_section('Cloud Create'))
 
     def node_sizes(self, all_sizes):
+        """Finds all acceptable NodeSizes for our installation.
+
+        Returns a list of (NodeSize, kwargs) pairs for each NodeSize object
+        returned by libcloud that matches a size listed in our config file.
+        """
+
         size_kwargs = {}
         for sec_name in self.sections():
             sec_words = sec_name.split(None, 2)
             if sec_words[0] != 'Size':
                 continue
-            size_kwargs[sec_words[1]] = self.get_section(sec_name, int)
-        return [(size, size_kwargs[size.id]) for size in all_sizes
-                if size.id in size_kwargs]
+            size_spec = self.get_section(sec_name, int)
+            if 'price' in size_spec:
+                size_spec['price'] = float(size_spec['price'])
+            size_kwargs[sec_words[1]] = size_spec
+        # EC2 node sizes are identified by id. GCE sizes are identified by name.
+        matching_sizes = []
+        for size in all_sizes:
+            if size.id in size_kwargs:
+                matching_sizes.append((size, size_kwargs[size.id]))
+            elif size.name in size_kwargs:
+                matching_sizes.append((size, size_kwargs[size.name]))
+        return matching_sizes
 
     def shutdown_windows(self):
         return [int(n)