- initialize bwlimit appropriately
[nodemanager.git] / sm.py
1 """Sliver manager.
2
3 The sliver manager has several functions.  It is responsible for
4 creating, resource limiting, starting, stopping, and destroying
5 slivers.  It provides an API for users to access these functions and
6 also to make inter-sliver resource loans.  The sliver manager is also
7 responsible for handling delegation accounts.
8 """
9
10 import bwlimit
11 import accounts
12 import api
13 import database
14 import delegate
15 import logger
16 import sliver_vs
17 import sioc
18
19
20 DEFAULT_ALLOCATION = {
21     'enabled': 1,
22     # CPU parameters
23     'cpu_min': 0, # ms/s
24     'cpu_share': 32, # proportional share
25     # bandwidth parameters
26     'net_min': bwlimit.bwmin, # bps
27     'net_max': bwlimit.bwmax, # bps
28     'net_share': 1, # proportional share
29     # bandwidth parameters over routes exempt from node bandwidth limits
30     'net2_min': bwlimit.bwmin, # bps
31     'net2_max': bwlimit.bwmax, # bps
32     'net2_share': 1, # proportional share
33     'disk_max': 5000000 # bytes
34     }
35
36 start_requested = False  # set to True in order to request that all slivers be started
37
38
39 @database.synchronized
40 def GetSlivers_callback(data, fullupdate=True):
41     """This function has two purposes.  One, convert GetSlivers() data into a more convenient format.  Two, even if no updates are coming in, use the GetSlivers() heartbeat as a cue to scan for expired slivers."""
42
43     node_id = None
44     try:
45         f = open('/etc/planetlab/node_id')
46         try: node_id = int(f.read())
47         finally: f.close()
48     except: logger.log_exc()
49
50     # query running network interfaces
51     devs = sioc.gifconf()
52     ips = dict(zip(devs.values(), devs.keys()))
53     macs = {}
54     for dev in devs:
55         macs[sioc.gifhwaddr(dev).lower()] = dev
56
57     for d in data:
58         if d['node_id'] != node_id: continue
59
60         # XXX Exempt Internet2 destinations from node bwlimits
61         # bwlimit.exempt_init('Internet2', internet2_ips)
62
63         for network in d['networks']:
64             if macs.has_key(network['mac'].lower()):
65                 dev = macs[network['mac'].lower()]
66             elif ips.has_key(network['ip']):
67                 dev = ips[network['ip']]
68             else:
69                 logger.log('%s: no such interface with address %s/%s' % (self.name, network['ip'], network['mac']))
70                 continue
71
72             try:
73                 old_bwlimit = bwlimit.get_bwcap(dev)
74             except:
75                 old_bwlimit = None
76
77             if network['bwlimit'] is None:
78                 new_bwlimit = bwlimit.bwmax
79             else:
80                 new_bwlimit = network['bwlimit']
81
82             if old_bwlimit != new_bwlimit:
83                 # reinitialize bandwidth limits
84                 bwlimit.init(dev, new_bwlimit)
85
86                 # XXX this should trigger an rspec refresh in case
87                 # some previously invalid sliver bwlimit is now valid
88                 # again, or vice-versa.
89
90         for sliver in d['slivers']:
91             rec = sliver.copy()
92             rec.setdefault('timestamp', d['timestamp'])
93
94             # convert attributes field to a proper dict
95             attr_dict = {}
96             for attr in rec.pop('attributes'): attr_dict[attr['name']] = attr['value']
97
98             # squash keys
99             keys = rec.pop('keys')
100             rec.setdefault('keys', '\n'.join([key_struct['key'] for key_struct in keys]))
101
102             rec.setdefault('type', attr_dict.get('type', 'sliver.VServer'))
103             rec.setdefault('vref', attr_dict.get('vref', 'default'))
104             rec.setdefault('initscript', attr_dict.get('initscript', ''))
105             rec.setdefault('delegations', [])  # XXX - delegation not yet supported
106
107             # extract the implied rspec
108             rspec = {}
109             rec['rspec'] = rspec
110             for resname, default_amt in DEFAULT_ALLOCATION.iteritems():
111                 try: amt = int(attr_dict[resname])
112                 except (KeyError, ValueError): amt = default_amt
113                 rspec[resname] = amt
114             database.db.deliver_record(rec)
115         if fullupdate: database.db.set_min_timestamp(d['timestamp'])
116     database.db.sync()
117
118     # handle requested startup
119     global start_requested
120     if start_requested:
121         start_requested = False
122         cumulative_delay = 0
123         for name in database.db.iterkeys():
124             accounts.get(name).start(delay=cumulative_delay)
125             cumulative_delay += 3
126
127 def deliver_ticket(data): return GetSlivers_callback(data, fullupdate=False)
128
129
130 def start(options, config):
131     accounts.register_class(sliver_vs.Sliver_VS)
132     accounts.register_class(delegate.Delegate)
133     global start_requested
134     start_requested = options.startup
135     database.start()
136     api.deliver_ticket = deliver_ticket
137     api.start()