PageRenderTime 53ms CodeModel.GetById 19ms RepoModel.GetById 0ms app.codeStats 0ms

/plugins/ipv6.py

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