3 from __future__ import absolute_import, print_function
5 import libcloud.common.types as cloud_types
7 from ...config import NETWORK_ERRORS
9 class BaseComputeNodeDriver(object):
10 """Abstract base class for compute node drivers.
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.
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.
22 CLOUD_ERRORS = NETWORK_ERRORS + (cloud_types.LibcloudError,)
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
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)
35 return super(BaseComputeNodeDriver, self).__getattr__(name)
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)()
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]
50 return self.real.list_nodes(**self.list_kwargs)
52 def arvados_create_kwargs(self, arvados_node):
53 raise NotImplementedError("BaseComputeNodeDriver.arvados_create_kwargs")
55 def create_node(self, size, arvados_node):
56 kwargs = self.create_kwargs.copy()
57 kwargs.update(self.arvados_create_kwargs(arvados_node))
59 return self.real.create_node(**kwargs)
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.
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")
75 def node_start_time(cls, node):
76 raise NotImplementedError("BaseComputeNodeDriver.node_start_time")
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)