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