Rewrite to make easier to read, maintain
[nodemanager-topo.git] / create-topo-attributes.py
1 # $Id$
2 # $URL$
3
4 """
5 Scan the VINI Central database and create topology "rspec" tags for
6 slices that have an EGRE key.  This script to be run from a cron job.
7 """
8
9 import string
10 import socket
11 from topology import links
12
13 class Node:
14     def __init__(self, node):
15         self.id = node['node_id']
16         self.hostname = node['hostname']
17         self.shortname = self.hostname.replace('.vini-veritas.net', '')
18         self.site_id = node['site_id']
19         self.ipaddr = socket.gethostbyname(self.hostname)
20
21     def get_link_id(self, remote):
22         if self.id < remote.id:
23             link = (self.id<<7) + remote.id
24         else:
25             link = (remote.id<<7) + self.id
26         return link
27         
28     def get_iface_id(self, remote):
29         if self.id < remote.id:
30             iface = 1
31         else:
32             iface = 2
33         return iface
34     
35     def get_virt_ip(self, remote):
36         link = self.get_link_id(remote)
37         iface = self.get_iface_id(remote)
38         first = link >> 6
39         second = ((link & 0x3f)<<2) + iface
40         return "192.168.%d.%d" % (first, second)
41
42     def get_virt_net(self, remote):
43         link = self.get_link_id(remote)
44         first = link >> 6
45         second = (link & 0x3f)<<2
46         return "192.168.%d.%d/30" % (first, second)
47         
48     def get_site(self, sites):
49         return sites[self.site_id]
50             
51     
52     # What nodes in the set of node_ids are adjacent to this one?
53     def adjacent_nodes(self, sites, nodes, node_ids):
54         mysite = self.get_site(sites)
55         adj_ids = mysite.adj_node_ids.intersection(node_ids)
56         adj_nodes = []
57         for id in adj_ids:
58             adj_nodes.append(nodes[id])
59         return adj_nodes
60     
61     def init_rspecs(self):
62         self.rspecs = []
63         
64     def add_rspec(self, remote):
65         my_ip = self.get_virt_ip(remote)
66         remote_ip = remote.get_virt_ip(self)
67         net = self.get_virt_net(remote)
68         rspec = remote.id, remote.ipaddr, "1Mbit", my_ip, remote_ip, net
69         self.rspecs.append(rspec)
70
71         
72 class Site:
73     def __init__(self, site):
74         self.id = site['site_id']
75         self.node_ids = site['node_ids']
76         self.adj_site_ids = set()
77         self.adj_node_ids = set()
78
79     def get_sitenodes(self, nodes):
80         n = []
81         for i in self.node_ids:
82             n.append(nodes[i])
83         return n
84     
85     def add_adjacency(self, site):
86         self.adj_site_ids.add(site.id)
87         for n in site.node_ids:
88             self.adj_node_ids.add(n)
89         
90     
91 class Slice:
92     def __init__(self, slice):
93         self.id = slice['slice_id']
94         self.name = slice['name']
95         self.node_ids = set(slice['node_ids'])
96         self.slice_tag_ids = slice['slice_tag_ids']
97         self.attrs = {}
98     
99     def get_tag(self, tagname, slicetags):
100         if tagname not in self.attrs:
101             for i in self.slice_tag_ids:
102                 if slicetags[i].tagname == tagname:
103                     self.attrs[tagname] = slicetags[i]
104                     break
105             else:
106                 self.attrs[tagname] = None
107         return self.attrs[tagname]
108         
109     def get_nodes(self, nodes):
110         n = []
111         for id in self.node_ids:
112             n.append(nodes[id])
113         return n
114     
115     def get_rspec(self, slicetags, node):
116         for i in self.slice_tag_ids:
117             tag = slicetags[i]
118             if tag.tagname == 'topo_rspec' and node.id == tag.node_id:
119                 return tag
120         else:
121             return None
122                 
123
124
125 class Slicetag:
126     def __init__(self, tag):
127         self.id = tag['slice_tag_id']
128         self.slice_id = tag['slice_id']
129         self.tagname = tag['tagname']
130         self.value = tag['value']
131         self.node_id = tag['node_id']
132         self.updated = 0
133       
134         
135                  
136
137 """
138 Create a dictionary of site objects keyed by site ID
139 """
140 def get_sites():
141     tmp = []
142     for site in GetSites():
143         t = site['site_id'], Site(site)
144         tmp.append(t)
145     return dict(tmp)
146
147
148 """
149 Create a dictionary of node objects keyed by node ID
150 """
151 def get_nodes():
152     tmp = []
153     for node in GetNodes():
154         t = node['node_id'], Node(node)
155         tmp.append(t)
156     return dict(tmp)
157
158 """
159 Create a dictionary of slice objects keyed by slice ID
160 """
161 def get_slices():
162     tmp = []
163     for slice in GetSlices():
164         t = slice['slice_id'], Slice(slice)
165         tmp.append(t)
166     return dict(tmp)
167
168 """
169 Create a dictionary of slicetag objects keyed by slice tag ID
170 """
171 def get_slice_tags():
172     tmp = []
173     for tag in GetSliceTags():
174         t = tag['slice_tag_id'], Slicetag(tag)
175         tmp.append(t)
176     return dict(tmp)
177     
178     
179 # For debugging
180 dryrun = 0
181
182 sites = get_sites()
183 nodes = get_nodes()
184 slices = get_slices()
185 slicetags = get_slice_tags()
186
187 # Add adjacencies
188 for (a, b) in links:
189     sites[a].add_adjacency(sites[b])
190     sites[b].add_adjacency(sites[a])  
191
192 for i in slices:
193     slice = slices[i]
194     tag = slice.get_tag('vini_topo', slicetags)
195     if tag:
196         topo_type = tag.value
197     else:
198         topo_type = None
199     
200     if topo_type == 'vsys' or topo_type == 'iias':
201         """ 
202         Assign EGRE key to the slice if needed
203         If no 'netns' attribute, add netns/1
204         For 'vsys', add vsys/setup-link and vsys/setup-nat
205         """
206         
207     if slice.get_tag('egre_key', slicetags) and topo_type == 'iias':
208         if dryrun:
209             print "Virtual topology for %s:" % slice.name
210             
211         hosts = "127.0.0.1\t\tlocalhost\n"
212         """
213         For each node in the slice, check whether the slice is running on any
214         adjacent nodes.  For each pair of adjacent nodes, add to nodes' rspecs.
215         """
216         for node in slice.get_nodes(nodes):
217             node.init_rspecs()
218             adj_nodes = node.adjacent_nodes(sites, nodes, slice.node_ids)
219             for adj in adj_nodes:
220                 node.add_rspec(adj)
221                 hosts += "%s\t\t%s\n" % (node.get_virt_ip(adj), node.shortname)
222                 
223             old_rspec = slice.get_rspec(slicetags, node)
224             if node.rspecs:
225                 topo_str = "%s" % node.rspecs
226
227                 if old_rspec:
228                     old_rspec.updated = 1
229                     id = old_rspec.id
230                     
231                 if old_rspec and old_rspec.value == topo_str:
232                         topo_str = "no change"
233                 elif not dryrun:
234                     if old_rspec:
235                         UpdateSliceTag(old_rspec.id, topo_str)
236                     else:
237                         AddSliceTag(slice.id, 'topo_rspec', topo_str, node.id)
238                         
239                 if dryrun:
240                     if not id:
241                         id = 'new'
242                     print "[%s] rspec for %s: %s" % (id, node.shortname, topo_str)
243                     
244         hosttag = slice.get_tag('hosts', slicetags)
245         if hosttag:
246             hosttag.updated = 1
247         if hosttag and hosts == hosttag.value:
248             hosts = "/etc/hosts: no change"
249         elif not dryrun:
250             if hosttag:
251                 UpdateSliceTag(hosttag.id, hosts)
252             else:
253                 AddSliceTag(slice.id, 'hosts', hosts)
254             
255         if dryrun:
256             print hosts
257
258     else:
259         if dryrun:
260             print "Slice %s not using IIAS" % slice.name
261
262 # Remove old topo_rspec entries
263 for i in slicetags:
264     tag = slicetags[i]
265     if (tag.tagname == 'topo_rspec' or tag.tagname == 'hosts') and not tag.updated:
266         if dryrun:
267             slice = slices[tag.slice_id].name
268             node = nodes[tag.node_id].hostname
269             print "Deleting topo_rspec tag %s (%s, %s)" % (tag.id, slice, node)
270         else:
271             DeleteSliceTag(tag.id)
272
273