debuggers.hg

view tools/python/xen/xend/server/netif.py @ 3639:ef59b38283a5

bitkeeper revision 1.1159.212.64 (42000361XrdUoEK_vD6RMga2eLmwZg)

Allow specification of backend vif MAC addresses. If a backend
MAC is not provided, generate one as before

Signed-off-by: Jody Belka <knew-xen@pimb.org>
Signed-off-by: ian.pratt@cl.cam.ac.uk
author iap10@labyrinth.cl.cam.ac.uk
date Tue Feb 01 22:32:01 2005 +0000 (2005-02-01)
parents 30ee9c427a5b
children bbe8541361dd
line source
1 # Copyright (C) 2004 Mike Wray <mike.wray@hp.com>
2 """Support for virtual network interfaces.
3 """
5 import random
7 from twisted.internet import defer
9 from xen.xend import sxp
10 from xen.xend import Vifctl
11 from xen.xend.XendError import XendError
12 from xen.xend.XendLogging import log
13 from xen.xend import XendVnet
14 from xen.xend.XendRoot import get_component
16 import channel
17 import controller
18 from messages import *
20 class NetifBackendController(controller.BackendController):
21 """Handler for the 'back-end' channel to a network device driver domain.
22 """
24 def __init__(self, ctrl, dom):
25 controller.BackendController.__init__(self, ctrl, dom)
26 self.addMethod(CMSG_NETIF_BE,
27 CMSG_NETIF_BE_DRIVER_STATUS,
28 self.recv_be_driver_status)
29 self.registerChannel()
31 def recv_be_driver_status(self, msg, req):
32 val = unpackMsg('netif_be_driver_status_t', msg)
33 status = val['status']
35 class NetifBackendInterface(controller.BackendInterface):
36 """Handler for the 'back-end' channel to a network device driver domain
37 on behalf of a front-end domain.
39 Each network device is handled separately, so we add no functionality
40 here.
41 """
43 pass
45 class NetifControllerFactory(controller.SplitControllerFactory):
46 """Factory for creating network interface controllers.
47 """
49 def __init__(self):
50 controller.SplitControllerFactory.__init__(self)
52 def createController(self, dom):
53 """Create a network interface controller for a domain.
55 @param dom: domain
56 @return: netif controller
57 """
58 return NetifController(self, dom)
60 def createBackendController(self, dom):
61 """Create a network device backend controller.
63 @param dom: backend domain
64 @return: backend controller
65 """
66 return NetifBackendController(self, dom)
68 def createBackendInterface(self, ctrl, dom, handle):
69 """Create a network device backend interface.
71 @param ctrl: controller
72 @param dom: backend domain
73 @param handle: interface handle
74 @return: backend interface
75 """
76 return NetifBackendInterface(ctrl, dom, handle)
78 def getDomainDevices(self, dom):
79 """Get the network devices for a domain.
81 @param dom: domain
82 @return: netif controller list
83 """
84 netif = self.getControllerByDom(dom)
85 return (netif and netif.getDevices()) or []
87 def getDomainDevice(self, dom, vif):
88 """Get a virtual network interface device for a domain.
90 @param dom: domain
91 @param vif: virtual interface index
92 @return: NetDev
93 """
94 netif = self.getControllerByDom(dom)
95 return (netif and netif.getDevice(vif)) or None
97 class NetDev(controller.SplitDev):
98 """Info record for a network device.
99 """
101 def __init__(self, vif, ctrl, config):
102 controller.SplitDev.__init__(self, vif, ctrl)
103 self.vif = vif
104 self.evtchn = None
105 self.configure(config)
106 self.status = NETIF_INTERFACE_STATUS_DISCONNECTED
108 def _get_config_mac(self, config):
109 vmac = sxp.child_value(config, 'mac')
110 if not vmac: return None
111 mac = [ int(x, 16) for x in vmac.split(':') ]
112 if len(mac) != 6: raise XendError("invalid mac: %s" % vmac)
113 return mac
115 def _get_config_be_mac(self, config):
116 vmac = sxp.child_value(config, 'be_mac')
117 if not vmac: return None
118 mac = [ int(x, 16) for x in vmac.split(':') ]
119 if len(mac) != 6: raise XendError("invalid backend mac: %s" % vmac)
120 return mac
122 def _get_config_ipaddr(self, config):
123 ips = sxp.children(config, elt='ip')
124 if ips:
125 val = []
126 for ipaddr in ips:
127 val.append(sxp.child0(ipaddr))
128 else:
129 val = None
130 return val
132 def configure(self, config, change=0):
133 if change:
134 return self.reconfigure(config)
135 self.config = config
136 self.mac = None
137 self.be_mac = None
138 self.bridge = None
139 self.script = None
140 self.ipaddr = []
142 mac = self._get_config_mac(config)
143 if mac is None:
144 raise XendError("invalid mac")
145 self.mac = mac
146 self.be_mac = self._get_config_be_mac(config)
147 self.bridge = sxp.child_value(config, 'bridge')
148 self.script = sxp.child_value(config, 'script')
149 self.ipaddr = self._get_config_ipaddr(config) or []
151 try:
152 xd = get_component('xen.xend.XendDomain')
153 self.backendDomain = int(xd.domain_lookup(sxp.child_value(config, 'backend', '0')).id)
154 except:
155 raise XendError('invalid backend domain')
157 def reconfigure(self, config):
158 """Reconfigure the interface with new values.
159 Not all configuration parameters can be changed:
160 bridge, script and ip addresses can,
161 backend and mac cannot.
163 To leave a parameter unchanged, omit it from the changes.
165 @param config configuration changes
166 @return updated interface configuration
167 @raise XendError on errors
168 """
169 changes = {}
170 mac = self._get_config_mac(config)
171 be_mac = self._get_config_be_mac(config)
172 bridge = sxp.child_value(config, 'bridge')
173 script = sxp.child_value(config, 'script')
174 ipaddr = self._get_config_ipaddr(config)
175 xd = get_component('xen.xend.XendDomain')
176 backendDomain = str(xd.domain_lookup(sxp.child_value(config, 'backend', '0')).id)
177 if (mac is not None) and (mac != self.mac):
178 raise XendError("cannot change mac")
179 if (be_mac is not None) and (be_mac != self.be_mac):
180 raise XendError("cannot change backend mac")
181 if (backendDomain is not None) and (backendDomain != str(self.backendDomain)):
182 raise XendError("cannot change backend")
183 if (bridge is not None) and (bridge != self.bridge):
184 changes['bridge'] = bridge
185 if (script is not None) and (script != self.script):
186 changes['script'] = script
187 if (ipaddr is not None) and (ipaddr != self.ipaddr):
188 changes['ipaddr'] = ipaddr
190 if changes:
191 self.vifctl("down")
192 for (k, v) in changes.items():
193 setattr(self, k, v)
194 self.config = sxp.merge(config, self.config)
195 self.vifctl("up")
196 return self.config
198 def sxpr(self):
199 vif = str(self.vif)
200 mac = self.get_mac()
201 val = ['vif',
202 ['idx', self.idx],
203 ['vif', vif],
204 ['mac', mac]]
205 if self.be_mac:
206 val.append(['be_mac', self.get_be_mac()])
207 if self.bridge:
208 val.append(['bridge', self.bridge])
209 if self.script:
210 val.append(['script', self.script])
211 for ip in self.ipaddr:
212 val.append(['ip', ip])
213 if self.evtchn:
214 val.append(['evtchn',
215 self.evtchn['port1'],
216 self.evtchn['port2']])
217 if self.index is not None:
218 val.append(['index', self.index])
219 return val
221 def get_vifname(self):
222 """Get the virtual interface device name.
223 """
224 return "vif%d.%d" % (self.controller.dom, self.vif)
226 def get_mac(self):
227 """Get the MAC address as a string.
228 """
229 return ':'.join(map(lambda x: "%02x" % x, self.mac))
231 def get_be_mac(self):
232 """Get the backend MAC address as a string.
233 """
234 return ':'.join(map(lambda x: "%02x" % x, self.be_mac))
236 def vifctl_params(self, vmname=None):
237 """Get the parameters to pass to vifctl.
238 """
239 dom = self.controller.dom
240 if vmname is None:
241 xd = get_component('xen.xend.XendDomain')
242 try:
243 vm = xd.domain_lookup(dom)
244 vmname = vm.name
245 except:
246 vmname = 'DOM%d' % dom
247 return { 'domain': vmname,
248 'vif' : self.get_vifname(),
249 'mac' : self.get_mac(),
250 'bridge': self.bridge,
251 'script': self.script,
252 'ipaddr': self.ipaddr, }
254 def vifctl(self, op, vmname=None):
255 """Bring the device up or down.
256 The vmname is needed when bringing a device up for a new domain because
257 the domain is not yet in the table so we can't look its name up.
259 @param op: operation name (up, down)
260 @param vmname: vmname
261 """
262 Vifctl.vifctl(op, **self.vifctl_params(vmname=vmname))
263 vnet = XendVnet.instance().vnet_of_bridge(self.bridge)
264 if vnet:
265 vnet.vifctl(op, self.get_vifname(), self.get_mac())
267 def attach(self):
268 d = self.send_be_create()
269 d.addCallback(self.respond_be_create)
270 return d
272 def getEventChannelBackend(self):
273 val = 0
274 if self.evtchn:
275 val = self.evtchn['port1']
276 return val
278 def getEventChannelFrontend(self):
279 val = 0
280 if self.evtchn:
281 val = self.evtchn['port2']
282 return val
284 def send_be_create(self):
285 d = defer.Deferred()
286 msg = packMsg('netif_be_create_t',
287 { 'domid' : self.controller.dom,
288 'netif_handle' : self.vif,
289 'be_mac' : self.be_mac or [0, 0, 0, 0, 0, 0],
290 'mac' : self.mac })
291 self.getBackendInterface().writeRequest(msg, response=d)
292 return d
294 def respond_be_create(self, msg):
295 val = unpackMsg('netif_be_create_t', msg)
296 return self
298 def destroy(self, change=0):
299 """Destroy the device's resources and disconnect from the back-end
300 device controller. If 'change' is true notify the front-end interface.
302 @param change: change flag
303 """
304 self.status = NETIF_INTERFACE_STATUS_CLOSED
305 def cb_destroy(val):
306 self.send_be_destroy()
307 self.getBackendInterface().close()
308 if change:
309 self.reportStatus()
310 log.debug("Destroying vif domain=%d vif=%d", self.controller.dom, self.vif)
311 self.vifctl('down')
312 d = self.send_be_disconnect()
313 d.addCallback(cb_destroy)
315 def send_be_disconnect(self):
316 d = defer.Deferred()
317 msg = packMsg('netif_be_disconnect_t',
318 { 'domid' : self.controller.dom,
319 'netif_handle' : self.vif })
320 self.getBackendInterface().writeRequest(msg, response=d)
321 return d
323 def send_be_destroy(self, response=None):
324 msg = packMsg('netif_be_destroy_t',
325 { 'domid' : self.controller.dom,
326 'netif_handle' : self.vif })
327 self.controller.delDevice(self.vif)
328 self.getBackendInterface().writeRequest(msg, response=response)
330 def recv_fe_interface_connect(self, val, req):
331 if not req: return
332 self.evtchn = channel.eventChannel(self.backendDomain, self.controller.dom)
333 msg = packMsg('netif_be_connect_t',
334 { 'domid' : self.controller.dom,
335 'netif_handle' : self.vif,
336 'evtchn' : self.getEventChannelBackend(),
337 'tx_shmem_frame' : val['tx_shmem_frame'],
338 'rx_shmem_frame' : val['rx_shmem_frame'] })
339 d = defer.Deferred()
340 d.addCallback(self.respond_be_connect)
341 self.getBackendInterface().writeRequest(msg, response=d)
343 def respond_be_connect(self, msg):
344 val = unpackMsg('netif_be_connect_t', msg)
345 dom = val['domid']
346 vif = val['netif_handle']
347 self.status = NETIF_INTERFACE_STATUS_CONNECTED
348 self.reportStatus()
350 def reportStatus(self, resp=0):
351 msg = packMsg('netif_fe_interface_status_t',
352 { 'handle' : self.vif,
353 'status' : self.status,
354 'evtchn' : self.getEventChannelFrontend(),
355 'domid' : self.backendDomain,
356 'mac' : self.mac })
357 if resp:
358 self.controller.writeResponse(msg)
359 else:
360 self.controller.writeRequest(msg)
362 def interfaceChanged(self):
363 """Notify the font-end that a device has been added or removed.
364 """
365 self.reportStatus()
367 class NetifController(controller.SplitController):
368 """Network interface controller. Handles all network devices for a domain.
369 """
371 def __init__(self, factory, dom):
372 controller.SplitController.__init__(self, factory, dom)
373 self.devices = {}
374 self.addMethod(CMSG_NETIF_FE,
375 CMSG_NETIF_FE_DRIVER_STATUS,
376 self.recv_fe_driver_status)
377 self.addMethod(CMSG_NETIF_FE,
378 CMSG_NETIF_FE_INTERFACE_STATUS,
379 self.recv_fe_interface_status)
380 self.addMethod(CMSG_NETIF_FE,
381 CMSG_NETIF_FE_INTERFACE_CONNECT,
382 self.recv_fe_interface_connect)
383 self.registerChannel()
385 def sxpr(self):
386 val = ['netif', ['dom', self.dom]]
387 return val
389 def lostChannel(self):
390 """Method called when the channel has been lost.
391 """
392 controller.Controller.lostChannel(self)
394 def addDevice(self, vif, config):
395 """Add a network interface.
397 @param vif: device index
398 @param config: device configuration
399 @return: device
400 """
401 if vif in self.devices:
402 raise XendError('device exists:' + str(vif))
403 dev = NetDev(vif, self, config)
404 self.devices[vif] = dev
405 return dev
407 def destroy(self):
408 """Destroy the controller and all devices.
409 """
410 self.destroyDevices()
412 def destroyDevices(self):
413 """Destroy all devices.
414 """
415 for dev in self.getDevices():
416 dev.destroy()
418 def attachDevice(self, vif, config, recreate=0):
419 """Attach a network device.
421 @param vif: interface index
422 @param config: device configuration
423 @param recreate: recreate flag (true after xend restart)
424 @return: deferred
425 """
426 dev = self.addDevice(vif, config)
427 if recreate:
428 d = defer.succeed(dev)
429 else:
430 d = dev.attach()
431 return d
433 def recv_fe_driver_status(self, msg, req):
434 if not req: return
435 print
436 print 'recv_fe_driver_status>'
437 msg = packMsg('netif_fe_driver_status_t',
438 { 'status' : NETIF_DRIVER_STATUS_UP,
439 ## FIXME: max_handle should be max active interface id
440 'max_handle' : len(self.devices)
441 #'max_handle' : self.getMaxDeviceIdx()
442 })
443 # Two ways of doing it:
444 # 1) front-end requests driver status, we reply with the interface count,
445 # front-end polls the interfaces,
446 # front-end checks they are all up
447 # 2) front-end requests driver status, we reply (with anything),
448 # we notify the interfaces,
449 # we notify driver status up with the count
450 # front-end checks they are all up
451 #
452 # We really want to use 1), but at the moment the xenU kernel panics
453 # in that mode, so we're sticking to 2) for now.
454 resp = 0
455 if resp:
456 self.writeResponse(msg)
457 else:
458 for dev in self.devices.values():
459 dev.reportStatus()
460 self.writeRequest(msg)
461 return resp
463 def recv_fe_interface_status(self, msg, req):
464 if not req: return
465 print
466 val = unpackMsg('netif_fe_interface_status_t', msg)
467 print "recv_fe_interface_status>", val
468 vif = val['handle']
469 dev = self.findDevice(vif)
470 if dev:
471 print 'recv_fe_interface_status>', 'dev=', dev
472 dev.reportStatus(resp=1)
473 else:
474 msg = packMsg('netif_fe_interface_status_t',
475 { 'handle' : -1,
476 'status' : NETIF_INTERFACE_STATUS_CLOSED,
477 });
478 print 'recv_fe_interface_status>', 'no dev, returning -1'
479 self.writeResponse(msg)
480 return 1
483 def recv_fe_interface_connect(self, msg, req):
484 val = unpackMsg('netif_fe_interface_connect_t', msg)
485 vif = val['handle']
486 print
487 print "recv_fe_interface_connect", val
488 dev = self.getDevice(vif)
489 if dev:
490 dev.recv_fe_interface_connect(val, req)
491 else:
492 log.error('Received netif_fe_interface_connect for unknown vif: dom=%d vif=%d',
493 self.dom, vif)