cbc4ea127e95aca2bd1712ba1c1f8e7eb54ff20d
[nodemanager.git] / plugins / ipv6.py
1 """
2 Description: IPv6 Support and Management to Slices
3 ipv6 nodemanager plugin
4 Version: 0.5
5 Author: Guilherme Sperb Machado <gsm@machados.org>
6 """
7
8 import logger
9 import os
10 import socket
11 import re
12
13 import tools
14 import libvirt
15 import uuid
16 from sliver_libvirt import Sliver_Libvirt
17 from xml.dom.minidom import parseString
18
19 priority=4
20
21 radvdConfFile = '/etc/radvd.conf'
22
23 def start():
24     logger.log("ipv6: plugin starting up...")
25
26 def buildLibvirtDefaultNetConfig(dom):
27
28         # create the <network> element
29         networkElem = dom.createElement("network")
30         # create <name> element
31         nameElem = dom.createElement("name")
32         textName = dom.createTextNode("default")
33         nameElem.appendChild(textName)
34         # create <uuid> element
35         uuidElem = dom.createElement("uuid")
36         textUUID = dom.createTextNode(str(uuid.uuid1()))
37         uuidElem.appendChild(textUUID)
38         # create <forward> element
39         forwardElem = dom.createElement("forward")
40         forwardElem.setAttribute("mode", "nat")
41         # create <nat> element
42         natElem = dom.createElement("nat")
43         # create <port> element
44         portElem = dom.createElement("port")
45         portElem.setAttribute("end", "65535")
46         portElem.setAttribute("start", "1024")
47         # create the ipv4 <ip> element
48         ipElem0 = dom.createElement("ip")
49         ipElem0.setAttribute("address", "192.168.122.1")
50         ipElem0.setAttribute("netmask", "255.255.255.0")
51         # create the <dhcp> element
52         dhcpElem = dom.createElement("dhcp")
53         # create the <range> element
54         rangeElem = dom.createElement("range")
55         rangeElem.setAttribute("end", "192.168.122.254")
56         rangeElem.setAttribute("start", "192.168.122.2")
57         # create the <bridge> element
58         bridgeElem = dom.createElement("bridge")
59         bridgeElem.setAttribute("delay", "0")
60         bridgeElem.setAttribute("name", "virbr0")
61         bridgeElem.setAttribute("stp", "on")
62
63         # build the whole thing
64         natElem.appendChild(portElem)
65         forwardElem.appendChild(natElem)
66
67         dhcpElem.appendChild(rangeElem)
68         ipElem0.appendChild(dhcpElem)
69         networkElem.appendChild(nameElem)
70         networkElem.appendChild(uuidElem)
71         networkElem.appendChild(forwardElem)
72         networkElem.appendChild(bridgeElem)
73         networkElem.appendChild(ipElem0)
74         return networkElem
75
76 def checkForIPv6(defaultNetworkConfig):
77         netnodes = defaultNetworkConfig.getElementsByTagName('network')
78         hasIPv6 = False
79         for netnode in netnodes:
80                 ips = netnode.getElementsByTagName('ip')
81                 for ip in ips:
82                         if ip.getAttribute('family')=='ipv6':
83                                 logger.log("ipv6: the configuration already have an IPv6 address/prefix set for the slivers! %s/%s" % (ip.getAttribute('address'), ip.getAttribute('prefix')) )
84                                 hasIPv6 = True
85         return hasIPv6
86
87
88
89 def addIPv6(defaultNetworkConfig, ipv6addr, prefix):
90
91         netnodes = defaultNetworkConfig.getElementsByTagName('network')
92         for netnode in netnodes:
93                 # create the ipv6 <ip> element 1
94                 ipElem1 = defaultNetworkConfig.createElement("ip")
95                 ipElem1.setAttribute("family", "ipv6")
96                 ipElem1.setAttribute("address", ipv6addr)
97                 ipElem1.setAttribute("prefix", prefix)
98                 # create the ipv6 <ip> element 2
99                 # it's ugly, I know, but we need a link-local address on the interface!
100                 ipElem2 = defaultNetworkConfig.createElement("ip")
101                 ipElem2.setAttribute("family", "ipv6")
102                 ipElem2.setAttribute("address", "fe80:1234::1")
103                 ipElem2.setAttribute("prefix", "64")
104                 # adding to the 'defaultNetworkConfig'
105                 netnode.appendChild(ipElem1)
106                 netnode.appendChild(ipElem2)
107         return defaultNetworkConfig
108
109 def changeIPv6(dom, ipv6addr, prefix):
110         ips = dom.getElementsByTagName('ip')
111         for ip in ips:
112                 if ip.getAttribute("family")=='ipv6' and not(re.match(r'fe80(.*)', ip.getAttribute("address"), re.I)):
113                         ip.setAttribute("address", ipv6addr)
114                         ip.setAttribute("prefix", prefix)
115         return dom
116
117
118 def removeIPv6(dom):
119         networks = dom.getElementsByTagName('network')
120         for network in networks:
121                 ips = network.getElementsByTagName('ip')
122                 for ip in ips:
123                         if ip.getAttribute("family")=='ipv6':
124                                 network.removeChild(ip)
125         return dom
126
127
128 def checkIfIPv6IsDifferent(dom, ipv6addr, prefix):
129         netnodes = dom.getElementsByTagName('network')
130         for netnode in netnodes:
131                 ips = netnode.getElementsByTagName('ip')
132                 for ip in ips:
133                         if ip.getAttribute('family')=='ipv6' and not( re.match(r'fe80(.*)', ip.getAttribute("address"), re.I) ) and (ip.getAttribute('address')!=ipv6addr or ip.getAttribute('prefix')!=prefix):
134                                 logger.log("ipv6: the IPv6 address or prefix are different. Change detected!")
135                                 return True
136
137         return False
138
139
140 def setAutostart(network):
141         try:
142                 network.setAutostart(1)
143         except:
144                 logger.log("ipv6: network could not set to autostart")
145
146
147 def setUp(networkLibvirt, connLibvirt, networkElem, ipv6addr, prefix):
148         newXml = networkElem.toxml()
149         #logger.log(networkElem.toxml())
150         #ret = dir(conn)
151         #for method in ret:
152         #       logger.log(repr(method))
153         networkLibvirt.undefine()
154         networkLibvirt.destroy()
155         connLibvirt.networkCreateXML(newXml)
156         networkDefault = connLibvirt.networkDefineXML(newXml)
157         setAutostart(networkDefault)
158         commandForwarding = ['sysctl', '-w', 'net.ipv6.conf.all.forwarding=1']
159         logger.log_call(commandForwarding, timeout=15*60)
160         configRadvd = """
161 interface virbr0
162 {
163         AdvSendAdvert on;
164         MinRtrAdvInterval 30;
165         MaxRtrAdvInterval 100;
166         prefix %s
167         {
168                 AdvOnLink on;
169                 AdvAutonomous on;
170                 AdvRouterAddr off;
171         };
172
173 };
174 """ % (ipv6addr+"/"+prefix)
175         f=open(radvdConfFile,'w')
176         f.write(configRadvd)
177         f.close()
178         killRadvd()
179         startRadvd()
180         logger.log("ipv6: set up process finalized. Enabled IPv6 address to the slivers!")
181
182 def cleanUp(networkLibvirt, connLibvirt, networkElem):
183     dom = removeIPv6(networkElem)
184     newXml = dom.toxml()
185     networkLibvirt.undefine()
186     networkLibvirt.destroy()
187     # TODO: set autostart for the network
188     connLibvirt.networkCreateXML(newXml)
189     networkDefault = connLibvirt.networkDefineXML(newXml)
190     setAutostart(networkDefault)
191     killRadvd()
192     logger.log("ipv6: cleanup process finalized. The IPv6 support on the slivers was removed.")
193
194 def killRadvd():
195     commandKillRadvd = ['killall', 'radvd']
196     logger.log_call(commandKillRadvd, timeout=15*60)
197
198 def startRadvd():
199         commandRadvd = ['radvd']
200         logger.log_call(commandRadvd, timeout=15*60)
201
202 def GetSlivers(data, config, plc):
203
204     #return
205     #for sliver in data['slivers']:
206         #ipv6addr,prefixlen = tools.get_sliver_ipv6(sliver['name'])
207         #tools.add_ipv6addr_hosts_line(sliver['name'], data['hostname'], ipv6addr)
208         #result = tools.search_ipv6addr_hosts(sliver['name'], ipv6addr)
209         #logger.log("tools: result=%s" % (str(result)) )
210         #tools.remove_all_ipv6addr_hosts(sliver['name'], data['hostname'])
211     #return
212
213
214     type = 'sliver.LXC'
215
216     interfaces = data['interfaces']
217     logger.log(repr(interfaces))
218     for interface in interfaces:
219         logger.log('ipv6: get interface 1: %r'%(interface))
220         if 'interface_tag_ids' in interface:
221                 interface_tag_ids = "interface_tag_ids"
222         interface_tag_id = "interface_tag_id"
223         settings = plc.GetInterfaceTags({interface_tag_id:interface[interface_tag_ids]})
224         isSliversIPv6PrefixSet = False
225         for setting in settings:
226                         #logger.log(repr(setting))
227             # TODO: create a static variable to describe the "sliversipv6prefix" tag
228                         if setting['tagname']=='sliversipv6prefix':
229                                 ipv6addrprefix = setting['value'].split('/', 1)
230                                 ipv6addr = ipv6addrprefix[0]
231                                 prefix = ipv6addrprefix[1]
232                                 logger.log("ipv6: %s" % (ipv6addr) )
233                                 validIPv6 = tools.isValidIPv6(ipv6addr)
234                                 if not(validIPv6):
235                                         logger.log("ipv6: the 'sliversipv6prefix' tag presented a non-valid IPv6 address!")
236                                 else:
237                                         # connecting to the libvirtd
238                                         connLibvirt = Sliver_Libvirt.getConnection(type)
239                                         list = connLibvirt.listAllNetworks()
240                                         for networkLibvirt in list:
241                                                 xmldesc = networkLibvirt.XMLDesc()
242                                                 dom = parseString(xmldesc)
243                                                 hasIPv6 = checkForIPv6(dom)
244                                                 if hasIPv6:
245                                                         # let's first check if the IPv6 is different or is it the same...
246                                                         isDifferent = checkIfIPv6IsDifferent(dom, ipv6addr, prefix)
247                                                         if isDifferent:
248                                                                 logger.log("ipv6: the tag 'sliversipv6prefix' was modified! Updating the configuration with the new one...")
249                                                                 networkElem = changeIPv6(dom, ipv6addr, prefix)
250                                                                 setUp(networkLibvirt, connLibvirt, networkElem, ipv6addr, prefix)
251                                                                 logger.log("ipv6: trying to reboot the slivers...")
252                                                                 tools.reboot_sliver('blah')
253                                                 else:
254                                                         logger.log("ipv6: starting to redefine the virtual network...")
255                                                         #networkElem = buildLibvirtDefaultNetConfig(dom,ipv6addr,prefix)
256                                                         networkElem = addIPv6(dom, ipv6addr, prefix)
257                                                         setUp(networkLibvirt, connLibvirt, networkElem, ipv6addr, prefix)
258                                                         logger.log("ipv6: trying to reboot the slivers...")
259                                                         tools.reboot_sliver('blah')
260                                 isSliversIPv6PrefixSet = True
261         if not(isSliversIPv6PrefixSet):
262                         # connecting to the libvirtd
263                         connLibvirt = Sliver_Libvirt.getConnection(type)
264                         list = connLibvirt.listAllNetworks()
265                         for networkLibvirt in list:
266                                 xmldesc = networkLibvirt.XMLDesc()
267                                 dom = parseString(xmldesc)
268                                 if checkForIPv6(dom):
269                                         cleanUp(networkLibvirt, connLibvirt, dom)
270                                         logger.log("ipv6: trying to reboot the slivers...")
271                                         tools.reboot_sliver('blah')
272
273     logger.log("ipv6: all done!")