263cf79767b9704a748ce1009ba5d888d6b6b850
[nodemanager.git] / plugins / privatebridge.py
1 #!/usr/bin/python
2
3 """ Private Bridge configurator.  """
4
5 import httplib
6 import os
7 import select
8 import shutil
9 import subprocess
10 import time
11 import tools
12
13 from threading import Thread
14 import logger
15 import tools
16
17 priority = 9
18
19 class OvsException (Exception) :
20     def __init__ (self, message="no message"):
21         self.message=message
22     def __repr__ (self): return message
23
24 def start():
25     logger.log('private bridge plugin starting up...')
26
27 def log_call_read(command,timeout=logger.default_timeout_minutes*60,poll=1):
28     message=" ".join(command)
29     logger.log("log_call: running command %s" % message)
30     logger.verbose("log_call: timeout=%r s" % timeout)
31     logger.verbose("log_call: poll=%r s" % poll)
32     trigger=time.time()+timeout
33     try:
34         child = subprocess.Popen(command, bufsize=1,
35                                  stdout=subprocess.PIPE, stderr=subprocess.PIPE, close_fds=True)
36
37         stdout = ""
38         while True:
39             # see if anything can be read within the poll interval
40             (r,w,x)=select.select([child.stdout],[],[],poll)
41             if r: stdout = stdout + child.stdout.read(1)
42             # is process over ?
43             returncode=child.poll()
44             # yes
45             if returncode != None:
46                 stdout = stdout + child.stdout.read()
47                 # child is done and return 0
48                 if returncode == 0:
49                     logger.log("log_call:end command (%s) completed" % message)
50                     if stdout != "":
51                         logger.log("log_call:stdout: %s" % stdout)
52                     return (returncode, stdout)
53                 # child has failed
54                 else:
55                     log("log_call:end command (%s) returned with code %d" %(message,returncode))
56                     return (returncode, stdout)
57             # no : still within timeout ?
58             if time.time() >= trigger:
59                 child.terminate()
60                 logger.log("log_call:end terminating command (%s) - exceeded timeout %d s"%(message,timeout))
61                 return (-2, None)
62                 break
63     except:
64         logger.log_exc("failed to run command %s" % message)
65
66     return (-1, None)
67
68 def ovs_vsctl(args):
69     return log_call_read(["ovs-vsctl"] + args)
70
71 def ovs_listbridge():
72     (returncode, stdout) = ovs_vsctl(["list-br"])
73     if (returncode != 0): raise OvsException("list-br")
74     return stdout.split()
75
76 def ovs_addbridge(name):
77     (returncode, stdout) = ovs_vsctl(["add-br",name])
78     if (returncode != 0): raise OvsException("add-br")
79
80 def ovs_listports(name):
81     (returncode, stdout) = ovs_vsctl(["list-ports", name])
82     if (returncode != 0): raise OvsException("list-ports")
83     return stdout.split()
84
85 def ovs_delbridge(name):
86     (returncode, stdout) = ovs_vsctl(["del-br",name])
87     if (returncode != 0): raise OvsException("del-br")
88
89 def ovs_addport(name, portname, type, remoteip, key):
90     args = ["add-port", name, portname, "--", "set", "interface", portname, "type="+type]
91     if remoteip:
92         args = args + ["options:remote_ip=" + remoteip]
93     if key:
94         args = args + ["options:key=" + str(key)]
95
96     (returncode, stdout) = ovs_vsctl(args)
97     if (returncode != 0): raise OvsException("add-port")
98
99 def ovs_delport(name, portname):
100     (returncode, stdout) = ovs_vsctl(["del-port",name,portname])
101     if (returncode != 0): raise OvsException("del-port")
102
103 def ensure_slicebridge_created(name, addr):
104     bridges = ovs_listbridge()
105     logger.log("privatebridge: current bridges = " + ",".join(bridges))
106     if name in bridges:
107         return
108
109     ovs_addbridge(name)
110
111     logger.log_call(["ifconfig", name, addr, "netmask", "255.0.0.0"])
112
113 def ensure_slicebridge_neighbors(name, sliver_id, neighbors):
114     ports = ovs_listports(name)
115
116     want_ports = []
117     for neighbor in neighbors:
118         (neighbor_node_id, neighbor_ip) = neighbor.split("/")
119         neighbor_node_id = int(neighbor_node_id)
120         portname = "gre%d-%d" % (sliver_id, neighbor_node_id)
121
122         want_ports.append(portname)
123
124         if not portname in ports:
125             ovs_addport(name, portname, "gre", neighbor_ip, sliver_id)
126
127     for portname in ports:
128         if portname.startswith("gre") and (portname not in want_ports):
129             ovs_delport(name, portname)
130
131 def configure_slicebridge(sliver, attributes):
132     sliver_name = sliver['name']
133     sliver_id = sliver['slice_id']
134
135     slice_bridge_name = attributes["slice_bridge_name"]
136
137     slice_bridge_addr = attributes.get("slice_bridge_addr", None)
138     if not slice_bridge_addr:
139         logger.log("privatebridge: no slice_bridge_addr for %s" % sliver_name)
140         return
141
142     slice_bridge_neighbors = attributes.get("slice_bridge_neighbors", None)
143     if not slice_bridge_neighbors:
144         logger.log("privatebridge: no slice_bridge_neighbors for %s" % sliver_name)
145         return
146
147     slice_bridge_neighbors = [x.strip() for x in slice_bridge_neighbors.split(",")]
148
149     ensure_slicebridge_created(slice_bridge_name, slice_bridge_addr)
150     ensure_slicebridge_neighbors(slice_bridge_name, sliver_id, slice_bridge_neighbors)
151
152 def GetSlivers(data, conf = None, plc = None):
153     node_id = tools.node_id()
154
155     if 'slivers' not in data:
156         logger.log_missing_data("privatebridge.GetSlivers",'slivers')
157         return
158
159     valid_bridges = []
160     for sliver in data['slivers']:
161         sliver_name = sliver['name']
162
163         # build a dict of attributes, because it's more convenient
164         attributes={}
165         for attribute in sliver['attributes']:
166             attributes[attribute['tagname']] = attribute['value']
167
168         bridge_name = attributes.get('slice_bridge_name',None)
169         if bridge_name:
170             configure_slicebridge(sliver, attributes)
171             valid_bridges.append(bridge_name)
172
173     # now, delete the bridges that we don't want
174     bridges = ovs_listbridge()
175     for bridge_name in bridges:
176         if not bridge_name.startswith("br-slice-"):
177             # ignore ones we didn't create
178             continue
179
180         if bridge_name in valid_bridges:
181             # ignore ones we want to keep
182             continue
183
184         logger.log("privatebridge: deleting unused bridge %s" % bridge_name)
185
186         ovs_delbridge(bridge_name)
187
188
189