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