125a0909c4450ce48ae789768bef35916669b405
[arvados.git] / services / nodemanager / arvnodeman / computenode / driver / gce.py
1 #!/usr/bin/env python
2
3 from __future__ import absolute_import, print_function
4
5 import functools
6 import json
7 import time
8
9 import libcloud.compute.base as cloud_base
10 import libcloud.compute.providers as cloud_provider
11 import libcloud.compute.types as cloud_types
12 from libcloud.compute.drivers import gce
13
14 from . import BaseComputeNodeDriver
15 from .. import arvados_node_fqdn
16
17 class ComputeNodeDriver(BaseComputeNodeDriver):
18     """Compute node driver wrapper for GCE
19
20     This translates cloud driver requests to GCE's specific parameters.
21     """
22     DEFAULT_DRIVER = cloud_provider.get_driver(cloud_types.Provider.GCE)
23     SEARCH_CACHE = {}
24     ssh_key = None
25     service_accounts = None
26
27     def __init__(self, auth_kwargs, list_kwargs, create_kwargs,
28                  driver_class=DEFAULT_DRIVER):
29         super(ComputeNodeDriver, self).__init__(
30             auth_kwargs, list_kwargs, create_kwargs,
31             driver_class)
32
33         for key in self.create_kwargs.keys():
34             init_method = getattr(self, '_init_' + key, None)
35             if init_method is not None:
36                 new_pair = init_method(self.create_kwargs.pop(key))
37                 if new_pair is not None:
38                     self.create_kwargs[new_pair[0]] = new_pair[1]
39
40     def _init_image_id(self, image_id):
41         return 'image', image_id
42
43     def _init_ping_host(self, ping_host):
44         self.ping_host = ping_host
45
46     def _init_service_accounts(self, service_accounts_str):
47         self.service_accounts = json.loads(service_accounts_str)
48
49     def _init_network_id(self, subnet_id):
50         return 'ex_network', subnet_id
51
52     def _init_ssh_key(self, filename):
53         with open(filename) as ssh_file:
54             self.ssh_key = ssh_file.read().strip()
55
56     def arvados_create_kwargs(self, arvados_node):
57         result = {'ex_metadata': self.list_kwargs.copy() }
58         ping_secret = arvados_node['info'].get('ping_secret')
59         if ping_secret is not None:
60             ping_url = ('https://{}/arvados/v1/nodes/{}/ping?ping_secret={}'.
61                         format(self.ping_host, arvados_node['uuid'],
62                                ping_secret))
63             result['ex_userdata'] = ping_url
64         if self.service_accounts is not None:
65             result['ex_service_accounts'] = self.service_accounts
66
67         # SSH keys are delivered to GCE nodes via ex_metadata: see
68         # http://stackoverflow.com/questions/26752617/creating-sshkeys-for-gce-instance-using-libcloud
69         if self.ssh_key is not None:
70             result['ex_metadata']['sshKeys'] = 'root:{}'.format(self.ssh_key)
71         return result
72
73     # When an Arvados node is synced with a GCE node, the Arvados hostname
74     # is forwarded in a GCE tag 'hostname-foo'.
75     # TODO(twp): implement an ex_set_metadata method (at least until
76     # libcloud supports the API setMetadata method) so we can pass this
77     # sensibly in the node metadata.
78     def sync_node(self, cloud_node, arvados_node):
79         tags = ['hostname-{}'.format(arvados_node_fqdn(arvados_node))]
80         self.real.ex_set_node_tags(cloud_node, tags)
81
82     @classmethod
83     def node_start_time(cls, node):
84         time_str = node.extra['launch_time'].split('.', 2)[0] + 'UTC'
85         return time.mktime(time.strptime(
86                 time_str,'%Y-%m-%dT%H:%M:%S%Z')) - time.timezone