Merge branch 'master' into 7167-keep-rsync-test-setup
[arvados.git] / services / nodemanager / arvnodeman / computenode / driver / azure.py
1 #!/usr/bin/env python
2
3 from __future__ import absolute_import, print_function
4
5 import time
6
7 import libcloud.compute.base as cloud_base
8 import libcloud.compute.providers as cloud_provider
9 import libcloud.compute.types as cloud_types
10 from libcloud.common.exceptions import BaseHTTPError
11
12 from . import BaseComputeNodeDriver
13 from .. import arvados_node_fqdn, arvados_timestamp, ARVADOS_TIMEFMT
14
15 class ComputeNodeDriver(BaseComputeNodeDriver):
16
17     DEFAULT_DRIVER = cloud_provider.get_driver(cloud_types.Provider.AZURE_ARM)
18     SEARCH_CACHE = {}
19     CLOUD_ERRORS = BaseComputeNodeDriver.CLOUD_ERRORS + (BaseHTTPError,)
20
21     def __init__(self, auth_kwargs, list_kwargs, create_kwargs,
22                  driver_class=DEFAULT_DRIVER):
23
24         if not list_kwargs.get("ex_resource_group"):
25             raise Exception("Must include ex_resource_group in Cloud List configuration (list_kwargs)")
26
27         create_kwargs["ex_resource_group"] = list_kwargs["ex_resource_group"]
28
29         self.tags = {key[4:]: value
30                      for key, value in create_kwargs.iteritems()
31                      if key.startswith('tag_')}
32         # filter out tags from create_kwargs
33         create_kwargs = {key: value
34                          for key, value in create_kwargs.iteritems()
35                          if not key.startswith('tag_')}
36         super(ComputeNodeDriver, self).__init__(
37             auth_kwargs, list_kwargs, create_kwargs,
38             driver_class)
39
40     def arvados_create_kwargs(self, arvados_node):
41         cluster_id, _, node_id = arvados_node['uuid'].split('-')
42         name = 'compute-{}-{}'.format(node_id, cluster_id)
43         tags = {
44             'booted_at': time.strftime(ARVADOS_TIMEFMT, time.gmtime()),
45             'arv-ping-url': self._make_ping_url(arvados_node)
46         }
47         tags.update(self.tags)
48         return {
49             'name': name,
50             'ex_tags': tags,
51         }
52
53     def sync_node(self, cloud_node, arvados_node):
54         try:
55             self.real.ex_create_tags(cloud_node,
56                                      {'hostname': arvados_node_fqdn(arvados_node)})
57             return True
58         except BaseHTTPError as b:
59             return False
60
61     def _init_image(self, urn):
62         return "image", self.get_image(urn)
63
64     def post_create_node(self, cloud_node):
65         self.real.ex_run_command(cloud_node,
66                                  """bash -c '
67                                  mkdir -p /var/tmp/arv-node-data/meta-data
68                                  echo "%s" > /var/tmp/arv-node-data/arv-ping-url
69                                  echo "%s" > /var/tmp/arv-node-data/meta-data/instance-id
70                                  echo "%s" > /var/tmp/arv-node-data/meta-data/instance-type
71                                  echo "%s" > /var/tmp/arv-node-data/meta-data/local-ipv4
72                                  '""" % (cloud_node.extra["tags"]["arv-ping-url"],
73                                          cloud_node.id,
74                                          cloud_node.extra["properties"]["hardwareProfile"]["vmSize"],
75                                          cloud_node.private_ips[0]),
76                                  timestamp=int(time.time()))
77
78     def list_nodes(self):
79         # Azure only supports filtering node lists by resource group.
80         # Do our own filtering based on tag.
81         return [node for node in
82                 super(ComputeNodeDriver, self).list_nodes()
83                 if node.extra["tags"].get("arvados-class") == self.tags["arvados-class"]]
84
85     def broken(self, cloud_node):
86         """Return true if libcloud has indicated the node is in a "broken" state."""
87         # UNKNOWN means the node state is unrecognized, which in practice means some combination
88         # of failure that the Azure libcloud driver doesn't know how to interpret.
89         return (cloud_node.state in (cloud_types.NodeState.ERROR, cloud_types.NodeState.UNKNOWN))
90
91     @classmethod
92     def node_fqdn(cls, node):
93         return node.extra["tags"].get("hostname")
94
95     @classmethod
96     def node_start_time(cls, node):
97         return arvados_timestamp(node.extra["tags"].get("booted_at"))