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