4f5d39f216742797ae094c7b5fe5c297980bfe30
[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
220     interfaces = data['interfaces']
221     logger.log(repr(interfaces))
222     for interface in interfaces:
223         logger.log('ipv6: get interface: %r'%(interface))
224         if 'interface_tag_ids' in interface:
225             interface_tag_ids = "interface_tag_ids"
226             interface_tag_id = "interface_tag_id"
227             settings = plc.GetInterfaceTags({interface_tag_id:interface[interface_tag_ids]})
228             is_slivers_ipv6_prefix_set = False
229             for setting in settings:
230                 if setting['tagname']==sliversipv6prefixtag:
231                     ipv6addrprefix = setting['value'].split('/', 1)
232                     ipv6addr = ipv6addrprefix[0]
233                     valid_prefix = False
234                     logger.log("ipv6: len(ipv6addrprefix)=%s" % (len(ipv6addrprefix)) )
235                     if len(ipv6addrprefix)>1:
236                         prefix = ipv6addrprefix[1]
237                         logger.log("ipv6: prefix=%s" % (prefix) )
238                         if int(prefix)>0 and int(prefix)<=64:
239                             valid_prefix = True
240                         else:
241                             valid_prefix = False
242                     else:
243                         valid_prefix = False
244                     logger.log("ipv6: '%s'=%s" % (sliversipv6prefixtag,ipv6addr) )
245                     valid_ipv6 = tools.is_valid_ipv6(ipv6addr)
246                     if not(valid_ipv6):
247                         logger.log("ipv6: the 'sliversipv6prefix' tag presented a non-valid IPv6 address!")
248                     elif not(valid_prefix):
249                             logger.log("ipv6: the '%s' tag does not present a valid prefix (e.g., '/64', '/58')!" % \
250                                        (sliversipv6prefixtag))
251                     else:
252                         # connecting to the libvirtd
253                         connLibvirt = Sliver_Libvirt.getConnection(type)
254                         list = connLibvirt.listAllNetworks()
255                         for networkLibvirt in list:
256                             xmldesc = networkLibvirt.XMLDesc()
257                             dom = parseString(xmldesc)
258                             has_ipv6 = check_for_ipv6(dom)
259                             if has_ipv6:
260                                 # let's first check if the IPv6 is different or is it the same...
261                                 is_different = check_if_ipv6_is_different(dom, ipv6addr, prefix)
262                                 if is_different:
263                                     logger.log("ipv6: tag 'sliversipv6prefix' was modified! " +
264                                            "Updating configuration with the new one...")
265                                     network_elem = change_ipv6(dom, ipv6addr, prefix)
266                                     set_up(networkLibvirt, connLibvirt, network_elem, ipv6addr, prefix)
267                                     logger.log("ipv6: trying to reboot the slivers...")
268                                     tools.reboot_slivers()
269                             else:
270                                 logger.log("ipv6: starting to redefine the virtual network...")
271                                 #network_elem = buildLibvirtDefaultNetConfig(dom,ipv6addr,prefix)
272                                 network_elem = add_ipv6(dom, ipv6addr, prefix)
273                                 set_up(networkLibvirt, connLibvirt, network_elem, ipv6addr, prefix)
274                                 logger.log("ipv6: trying to reboot the slivers...")
275                                 tools.reboot_slivers()
276                         is_slivers_ipv6_prefix_set = True
277             if not(is_slivers_ipv6_prefix_set):
278                 # connecting to the libvirtd
279                 connLibvirt = Sliver_Libvirt.getConnection(type)
280                 list = connLibvirt.listAllNetworks()
281                 for networkLibvirt in list:
282                     xmldesc = networkLibvirt.XMLDesc()
283                     dom = parseString(xmldesc)
284                     if check_for_ipv6(dom):
285                         clean_up(networkLibvirt, connLibvirt, dom)
286                         logger.log("ipv6: trying to reboot the slivers...")
287                         tools.reboot_slivers()
288
289     logger.log("ipv6: all done!")