Add 'apps/arv-web/' from commit 'f9732ad8460d013c2f28363655d0d1b91894dca5'
[arvados.git] / services / nodemanager / arvnodeman / computenode / driver / __init__.py
1 #!/usr/bin/env python
2
3 from __future__ import absolute_import, print_function
4
5 import libcloud.common.types as cloud_types
6
7 from ...config import NETWORK_ERRORS
8
9 class BaseComputeNodeDriver(object):
10     """Abstract base class for compute node drivers.
11
12     libcloud abstracts away many of the differences between cloud providers,
13     but managing compute nodes requires some cloud-specific features (e.g.,
14     on EC2 we use tags to identify compute nodes).  Compute node drivers
15     are responsible for translating the node manager's cloud requests to a
16     specific cloud's vocabulary.
17
18     Subclasses must implement arvados_create_kwargs (to update node
19     creation kwargs with information about the specific Arvados node
20     record), sync_node, and node_start_time.
21     """
22     CLOUD_ERRORS = NETWORK_ERRORS + (cloud_types.LibcloudError,)
23
24     def __init__(self, auth_kwargs, list_kwargs, create_kwargs, driver_class):
25         self.real = driver_class(**auth_kwargs)
26         self.list_kwargs = list_kwargs
27         self.create_kwargs = create_kwargs
28
29     def __getattr__(self, name):
30         # Proxy non-extension methods to the real driver.
31         if (not name.startswith('_') and not name.startswith('ex_')
32               and hasattr(self.real, name)):
33             return getattr(self.real, name)
34         else:
35             return super(BaseComputeNodeDriver, self).__getattr__(name)
36
37     def search_for(self, term, list_method, key=lambda item: item.id):
38         cache_key = (list_method, term)
39         if cache_key not in self.SEARCH_CACHE:
40             results = [item for item in getattr(self.real, list_method)()
41                        if key(item) == term]
42             count = len(results)
43             if count != 1:
44                 raise ValueError("{} returned {} results for '{}'".format(
45                         list_method, count, term))
46             self.SEARCH_CACHE[cache_key] = results[0]
47         return self.SEARCH_CACHE[cache_key]
48
49     def list_nodes(self):
50         return self.real.list_nodes(**self.list_kwargs)
51
52     def arvados_create_kwargs(self, arvados_node):
53         raise NotImplementedError("BaseComputeNodeDriver.arvados_create_kwargs")
54
55     def create_node(self, size, arvados_node):
56         kwargs = self.create_kwargs.copy()
57         kwargs.update(self.arvados_create_kwargs(arvados_node))
58         kwargs['size'] = size
59         return self.real.create_node(**kwargs)
60
61     def post_create_node(self, cloud_node):
62         # ComputeNodeSetupActor calls this method after the cloud node is
63         # created.  Any setup tasks that need to happen afterward (e.g.,
64         # tagging) should be done in this method.
65         pass
66
67     def sync_node(self, cloud_node, arvados_node):
68         # When a compute node first pings the API server, the API server
69         # will automatically assign some attributes on the corresponding
70         # node record, like hostname.  This method should propagate that
71         # information back to the cloud node appropriately.
72         raise NotImplementedError("BaseComputeNodeDriver.sync_node")
73
74     @classmethod
75     def node_start_time(cls, node):
76         raise NotImplementedError("BaseComputeNodeDriver.node_start_time")
77
78     @classmethod
79     def is_cloud_exception(cls, exception):
80         # libcloud compute drivers typically raise bare Exceptions to
81         # represent API errors.  Return True for any exception that is
82         # exactly an Exception, or a better-known higher-level exception.
83         return (isinstance(exception, cls.CLOUD_ERRORS) or
84                 getattr(exception, '__class__', None) is Exception)