closes #5243, #5194 Merge branch '5194-quickfix-disambiguate-gettingstarted-sections'
[arvados.git] / services / nodemanager / arvnodeman / computenode / __init__.py
1 #!/usr/bin/env python
2
3 from __future__ import absolute_import, print_function
4
5 import itertools
6 import time
7
8 ARVADOS_TIMEFMT = '%Y-%m-%dT%H:%M:%SZ'
9
10 def arvados_node_fqdn(arvados_node, default_hostname='dynamic.compute'):
11     hostname = arvados_node.get('hostname') or default_hostname
12     return '{}.{}'.format(hostname, arvados_node['domain'])
13
14 def arvados_node_mtime(node):
15     return arvados_timestamp(node['modified_at'])
16
17 def arvados_timestamp(timestr):
18     return time.mktime(time.strptime(timestr + 'UTC',
19                                      ARVADOS_TIMEFMT + '%Z')) - time.timezone
20
21 def timestamp_fresh(timestamp, fresh_time):
22     return (time.time() - timestamp) < fresh_time
23
24 class ShutdownTimer(object):
25     """Keep track of a cloud node's shutdown windows.
26
27     Instantiate this class with a timestamp of when a cloud node started,
28     and a list of durations (in minutes) of when the node must not and may
29     be shut down, alternating.  The class will tell you when a shutdown
30     window is open, and when the next open window will start.
31     """
32     def __init__(self, start_time, shutdown_windows):
33         # The implementation is easiest if we have an even number of windows,
34         # because then windows always alternate between open and closed.
35         # Rig that up: calculate the first shutdown window based on what's
36         # passed in.  Then, if we were given an odd number of windows, merge
37         # that first window into the last one, since they both# represent
38         # closed state.
39         first_window = shutdown_windows[0]
40         shutdown_windows = list(shutdown_windows[1:])
41         self._next_opening = start_time + (60 * first_window)
42         if len(shutdown_windows) % 2:
43             shutdown_windows.append(first_window)
44         else:
45             shutdown_windows[-1] += first_window
46         self.shutdown_windows = itertools.cycle([60 * n
47                                                  for n in shutdown_windows])
48         self._open_start = self._next_opening
49         self._open_for = next(self.shutdown_windows)
50
51     def _advance_opening(self):
52         while self._next_opening < time.time():
53             self._open_start = self._next_opening
54             self._next_opening += self._open_for + next(self.shutdown_windows)
55             self._open_for = next(self.shutdown_windows)
56
57     def next_opening(self):
58         self._advance_opening()
59         return self._next_opening
60
61     def window_open(self):
62         self._advance_opening()
63         return 0 < (time.time() - self._open_start) < self._open_for