ia64/xen-unstable

view tools/python/xen/xend/server/netif.py @ 6538:84ee014ebd41

Merge xen-vtx-unstable.hg
author adsharma@los-vmm.sc.intel.com
date Wed Aug 17 12:34:38 2005 -0800 (2005-08-17)
parents 23979fb12c49 c31e1abf4df2
children 99914b54f7bf
line source
1 #============================================================================
2 # This library is free software; you can redistribute it and/or
3 # modify it under the terms of version 2.1 of the GNU Lesser General Public
4 # License as published by the Free Software Foundation.
5 #
6 # This library is distributed in the hope that it will be useful,
7 # but WITHOUT ANY WARRANTY; without even the implied warranty of
8 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
9 # Lesser General Public License for more details.
10 #
11 # You should have received a copy of the GNU Lesser General Public
12 # License along with this library; if not, write to the Free Software
13 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
14 #============================================================================
15 # Copyright (C) 2004, 2005 Mike Wray <mike.wray@hp.com>
16 #============================================================================
18 """Support for virtual network interfaces.
19 """
21 import random
23 from xen.util.mac import macFromString, macToString
25 from xen.xend import sxp
26 from xen.xend import Vifctl
27 from xen.xend.XendError import XendError, VmError
28 from xen.xend.XendLogging import log
29 from xen.xend import XendVnet
30 from xen.xend.XendRoot import get_component
31 from xen.xend.xenstore import DBVar
33 from xen.xend.server import channel
34 from xen.xend.server.controller import CtrlMsgRcvr, Dev, DevController
35 from xen.xend.server.messages import *
37 class NetDev(Dev):
38 """A network device.
39 """
41 # State:
42 # inherited +
43 # ./config
44 # ./mac
45 # ./be_mac
46 # ./bridge
47 # ./script
48 # ./ipaddr ?
49 #
50 # ./credit
51 # ./period
52 #
53 # ./vifctl: up/down?
54 # ./vifname
55 #
56 #
57 # Poss should have no backend state here - except for ref to backend's own tree
58 # for the device? And a status - the one we want.
59 # ./back/dom
60 # ./back/devid - id for back-end (netif_handle) - same as front/devid
61 # ./back/id - backend id (if more than one b/e per domain)
62 # ./back/status
63 # ./back/tx_shmem_frame - actually these belong in back-end state
64 # ./back/rx_shmem_frame
65 #
66 # ./front/dom
67 # ./front/devid
68 # ./front/status - need 2: one for requested, one for actual? Or drive from dev status
69 # and this is front status only.
70 # ./front/tx_shmem_frame
71 # ./front/rx_shmem_frame
72 #
73 # ./evtchn/front - here or in front/back?
74 # ./evtchn/back
75 # ./evtchn/status ?
76 # At present created by dev: but should be created unbound by front/back
77 # separately and then bound (by back)?
79 __exports__ = Dev.__exports__ + [
80 DBVar('config', ty='sxpr'),
81 DBVar('mac', ty='mac'),
82 DBVar('be_mac', ty='mac'),
83 DBVar('bridge', ty='str'),
84 DBVar('script', ty='str'),
85 DBVar('credit', ty='int'),
86 DBVar('period', ty='int'),
87 DBVar('vifname', ty='str'),
88 ]
90 def __init__(self, controller, id, config, recreate=False):
91 Dev.__init__(self, controller, id, config, recreate=recreate)
92 self.vif = int(self.id)
93 self.evtchn = None
94 self.status = None
95 self.frontendDomain = self.getDomain()
96 self.frontendChannel = None
97 self.backendDomain = None
98 self.backendChannel = None
99 self.credit = None
100 self.period = None
101 self.mac = None
102 self.be_mac = None
103 self.bridge = None
104 self.script = None
105 self.ipaddr = None
106 self.mtu = None
107 self.vifname = None
108 self.configure(self.config, recreate=recreate)
110 def exportToDB(self, save=False):
111 Dev.exportToDB(self, save=save)
112 if self.evtchn:
113 db = self.db.addChild("evtchn")
114 self.evtchn.saveToDB(db, save=save)
116 def init(self, recreate=False, reboot=False):
117 self.destroyed = False
118 self.status = NETIF_INTERFACE_STATUS_DISCONNECTED
119 self.frontendDomain = self.getDomain()
120 self.frontendChannel = self.getChannel()
121 cf = channel.channelFactory()
122 self.backendChannel = cf.openChannel(self.backendDomain)
124 def _get_config_mac(self, config):
125 vmac = sxp.child_value(config, 'mac')
126 if not vmac: return None
127 try:
128 mac = macFromString(vmac)
129 except:
130 raise XendError("invalid mac: %s" % vmac)
131 return mac
133 def _get_config_be_mac(self, config):
134 vmac = sxp.child_value(config, 'be_mac')
135 if not vmac: return None
136 try:
137 mac = macFromString(vmac)
138 except:
139 raise XendError("invalid backend mac: %s" % vmac)
140 return mac
142 def _get_config_ipaddr(self, config):
143 ips = sxp.children(config, elt='ip')
144 if ips:
145 val = []
146 for ipaddr in ips:
147 val.append(sxp.child0(ipaddr))
148 else:
149 val = None
150 return val
152 def _get_config_mtu(self, config):
153 mtu = sxp.child_value(config, 'mtu')
154 if not mtu: return None
155 try:
156 mtu = int(mtu)
157 except:
158 raise XendError("invalid mtu: %s" & mtu)
159 return mtu
161 def configure(self, config, change=False, recreate=False):
162 if change:
163 return self.reconfigure(config)
164 self.config = config
165 self.mac = None
166 self.be_mac = None
167 self.bridge = None
168 self.script = None
169 self.ipaddr = []
170 self.vifname = None
172 self.vifname = sxp.child_value(config, 'vifname')
173 if self.vifname is None:
174 self.vifname = self.default_vifname()
175 if len(self.vifname) > 15:
176 raise XendError('invalid vifname: too long: ' + self.vifname)
177 mac = self._get_config_mac(config)
178 if mac is None:
179 raise XendError("invalid mac")
180 self.mac = mac
181 self.be_mac = self._get_config_be_mac(config)
182 self.bridge = sxp.child_value(config, 'bridge')
183 self.script = sxp.child_value(config, 'script')
184 self.ipaddr = self._get_config_ipaddr(config) or []
185 self.mtu = self._get_config_mtu(config)
186 self._config_credit_limit(config)
188 try:
189 if recreate:
190 self.backendDomain = int(sxp.child_value(config, 'backend', '0'))
191 else:
192 #todo: Code below will fail on xend restart when backend is not domain 0.
193 xd = get_component('xen.xend.XendDomain')
194 self.backendDomain = xd.domain_lookup_by_name(sxp.child_value(config, 'backend', '0')).id
195 except:
196 raise XendError('invalid backend domain')
197 return self.config
199 def reconfigure(self, config):
200 """Reconfigure the interface with new values.
201 Not all configuration parameters can be changed:
202 bridge, script and ip addresses can,
203 backend and mac cannot.
205 To leave a parameter unchanged, omit it from the changes.
207 @param config configuration changes
208 @return updated interface configuration
209 @raise XendError on errors
210 """
211 changes = {}
212 mac = self._get_config_mac(config)
213 be_mac = self._get_config_be_mac(config)
214 bridge = sxp.child_value(config, 'bridge')
215 script = sxp.child_value(config, 'script')
216 ipaddr = self._get_config_ipaddr(config)
217 mtu = self._get_config_mtu(config)
219 xd = get_component('xen.xend.XendDomain')
220 backendDomain = xd.domain_lookup_by_name(sxp.child_value(config, 'backend', '0')).id
222 if (mac is not None) and (mac != self.mac):
223 raise XendError("cannot change mac")
224 if (be_mac is not None) and (be_mac != self.be_mac):
225 raise XendError("cannot change backend mac")
226 if (backendDomain is not None) and (backendDomain != self.backendDomain):
227 raise XendError("cannot change backend")
228 if (bridge is not None) and (bridge != self.bridge):
229 changes['bridge'] = bridge
230 if (script is not None) and (script != self.script):
231 changes['script'] = script
232 if (ipaddr is not None) and (ipaddr != self.ipaddr):
233 changes['ipaddr'] = ipaddr
234 if (mtu is not None) and (mtu != self.mtu):
235 changes['mtu'] = mtu
237 if changes:
238 self.vifctl("down")
239 for (k, v) in changes.items():
240 setattr(self, k, v)
241 self.config = sxp.merge(config, self.config)
242 self.vifctl("up")
244 self._config_credit_limit(config, change=True)
245 return self.config
247 def _config_credit_limit(self, config, change=False):
248 period = sxp.child_value(config, 'period')
249 credit = sxp.child_value(config, 'credit')
250 if period and credit:
251 try:
252 period = int(period)
253 credit = int(credit)
254 except ex:
255 raise XendError('vif: invalid credit limit')
256 if change:
257 self.setCreditLimit(credit, period)
258 self.config = sxp.merge([sxp.name(self.config),
259 ['credit', credit],
260 ['period', period]],
261 self.config)
262 else:
263 self.period = period
264 self.credit = credit
265 elif period or credit:
266 raise XendError('vif: invalid credit limit')
268 def sxpr(self):
269 vif = str(self.vif)
270 mac = self.get_mac()
271 val = ['vif',
272 ['id', self.id],
273 ['vif', vif],
274 ['mac', mac],
275 ['vifname', self.vifname],
276 ]
278 if self.be_mac:
279 val.append(['be_mac', self.get_be_mac()])
280 if self.bridge:
281 val.append(['bridge', self.bridge])
282 if self.script:
283 val.append(['script', self.script])
284 for ip in self.ipaddr:
285 val.append(['ip', ip])
286 if self.credit:
287 val.append(['credit', self.credit])
288 if self.period:
289 val.append(['period', self.period])
290 if self.evtchn:
291 val.append(['evtchn',
292 self.evtchn['port1'],
293 self.evtchn['port2']])
294 return val
296 def get_vifname(self):
297 """Get the virtual interface device name.
298 """
299 return self.vifname
301 def default_vifname(self):
302 return "vif%d.%d" % (self.frontendDomain, self.vif)
304 def get_mac(self):
305 """Get the MAC address as a string.
306 """
307 return macToString(self.mac)
309 def get_be_mac(self):
310 """Get the backend MAC address as a string.
311 """
312 return macToString(self.be_mac)
314 def vifctl_params(self, vmname=None):
315 """Get the parameters to pass to vifctl.
316 """
317 dom = self.frontendDomain
318 if vmname is None:
319 xd = get_component('xen.xend.XendDomain')
320 try:
321 vm = xd.domain_lookup(dom)
322 vmname = vm.name
323 except:
324 vmname = 'Domain-%d' % dom
325 return { 'domain': vmname,
326 'vif' : self.get_vifname(),
327 'mac' : self.get_mac(),
328 'bridge': self.bridge,
329 'script': self.script,
330 'ipaddr': self.ipaddr, }
332 def vifctl(self, op, vmname=None):
333 """Bring the device up or down.
334 The vmname is needed when bringing a device up for a new domain because
335 the domain is not yet in the table so we can't look its name up.
337 @param op: operation name (up, down)
338 @param vmname: vmname
339 """
340 if op == 'up':
341 Vifctl.set_vif_name(self.default_vifname(), self.vifname)
342 Vifctl.vifctl(op, **self.vifctl_params(vmname=vmname))
343 vnet = XendVnet.instance().vnet_of_bridge(self.bridge)
344 if vnet:
345 vnet.vifctl(op, self.get_vifname(), self.get_mac())
347 def attach(self, recreate=False, change=False):
348 if recreate:
349 pass
350 else:
351 self.send_be_create()
352 if self.credit and self.period:
353 self.send_be_creditlimit(self.credit, self.period)
354 self.vifctl('up', vmname=self.getDomainName())
356 def closeEvtchn(self):
357 if self.evtchn:
358 channel.eventChannelClose(self.evtchn)
359 self.evtchn = None
361 def openEvtchn(self):
362 self.evtchn = channel.eventChannel(self.backendDomain, self.frontendDomain)
364 def getEventChannelBackend(self):
365 val = 0
366 if self.evtchn:
367 val = self.evtchn['port1']
368 return val
370 def getEventChannelFrontend(self):
371 val = 0
372 if self.evtchn:
373 val = self.evtchn['port2']
374 return val
376 def send_be_create(self):
377 msg = packMsg('netif_be_create_t',
378 { 'domid' : self.frontendDomain,
379 'netif_handle' : self.vif,
380 'be_mac' : self.be_mac or [0, 0, 0, 0, 0, 0],
381 'mac' : self.mac,
382 #'vifname' : self.vifname
383 })
384 msg = self.backendChannel.requestResponse(msg)
385 # todo: check return status
387 def destroy(self, change=False, reboot=False):
388 """Destroy the device's resources and disconnect from the back-end
389 device controller. If 'change' is true notify the front-end interface.
391 @param change: change flag
392 """
393 self.destroyed = True
394 self.status = NETIF_INTERFACE_STATUS_CLOSED
395 log.debug("Destroying vif domain=%d vif=%d", self.frontendDomain, self.vif)
396 self.closeEvtchn()
397 self.vifctl('down')
398 self.send_be_disconnect()
399 self.send_be_destroy()
400 if change:
401 self.reportStatus()
403 def send_be_disconnect(self):
404 msg = packMsg('netif_be_disconnect_t',
405 { 'domid' : self.frontendDomain,
406 'netif_handle' : self.vif })
407 self.backendChannel.requestResponse(msg)
408 #todo: check return status
410 def send_be_destroy(self, response=None):
411 msg = packMsg('netif_be_destroy_t',
412 { 'domid' : self.frontendDomain,
413 'netif_handle' : self.vif })
414 self.backendChannel.requestResponse(msg)
415 #todo: check return status
417 def recv_fe_interface_connect(self, val):
418 self.openEvtchn()
419 msg = packMsg('netif_be_connect_t',
420 { 'domid' : self.frontendDomain,
421 'netif_handle' : self.vif,
422 'evtchn' : self.getEventChannelBackend(),
423 'tx_shmem_frame' : val['tx_shmem_frame'],
424 'tx_shmem_ref' : val['tx_shmem_ref'],
425 'rx_shmem_frame' : val['rx_shmem_frame'],
426 'rx_shmem_ref' : val['rx_shmem_ref'] })
427 msg = self.backendChannel.requestResponse(msg)
428 #todo: check return status
429 self.status = NETIF_INTERFACE_STATUS_CONNECTED
430 self.reportStatus()
432 def setCreditLimit(self, credit, period):
433 #todo: these params should be in sxpr and vif config.
434 self.credit = credit
435 self.period = period
436 self.send_be_creditlimit(credit, period)
438 def getCredit(self):
439 return self.credit
441 def getPeriod(self):
442 return self.period
444 def send_be_creditlimit(self, credit, period):
445 msg = packMsg('netif_be_creditlimit_t',
446 { 'domid' : self.frontendDomain,
447 'netif_handle' : self.vif,
448 'credit_bytes' : credit,
449 'period_usec' : period })
450 msg = self.backendChannel.requestResponse(msg)
451 # todo: check return status
453 def reportStatus(self, resp=False):
454 msg = packMsg('netif_fe_interface_status_t',
455 { 'handle' : self.vif,
456 'status' : self.status,
457 'evtchn' : self.getEventChannelFrontend(),
458 'domid' : self.backendDomain,
459 'mac' : self.mac })
460 if resp:
461 self.frontendChannel.writeResponse(msg)
462 else:
463 self.frontendChannel.writeRequest(msg)
465 def interfaceChanged(self):
466 """Notify the front-end that a device has been added or removed.
467 """
468 self.reportStatus()
470 class NetifController(DevController):
471 """Network interface controller. Handles all network devices for a domain.
472 """
474 def __init__(self, vm, recreate=False):
475 DevController.__init__(self, vm, recreate=recreate)
476 self.channel = None
477 self.rcvr = None
478 self.channel = None
480 def initController(self, recreate=False, reboot=False):
481 self.destroyed = False
482 self.channel = self.getChannel()
483 # Register our handlers for incoming requests.
484 self.rcvr = CtrlMsgRcvr(self.channel)
485 self.rcvr.addHandler(CMSG_NETIF_FE,
486 CMSG_NETIF_FE_DRIVER_STATUS,
487 self.recv_fe_driver_status)
488 self.rcvr.addHandler(CMSG_NETIF_FE,
489 CMSG_NETIF_FE_INTERFACE_STATUS,
490 self.recv_fe_interface_status)
491 self.rcvr.addHandler(CMSG_NETIF_FE,
492 CMSG_NETIF_FE_INTERFACE_CONNECT,
493 self.recv_fe_interface_connect)
494 self.rcvr.registerChannel()
495 if reboot:
496 self.rebootDevices()
498 def destroyController(self, reboot=False):
499 """Destroy the controller and all devices.
500 """
501 self.destroyed = True
502 log.debug("Destroying netif domain=%d", self.getDomain())
503 self.destroyDevices(reboot=reboot)
504 if self.rcvr:
505 self.rcvr.deregisterChannel()
507 def sxpr(self):
508 val = ['netif', ['dom', self.getDomain()]]
509 return val
511 def newDevice(self, id, config, recreate=False):
512 """Create a network device.
514 @param id: interface id
515 @param config: device configuration
516 @param recreate: recreate flag (true after xend restart)
517 """
518 return NetDev(self, id, config, recreate=recreate)
520 def limitDevice(self, vif, credit, period):
521 if vif not in self.devices:
522 raise XendError('device does not exist for credit limit: vif'
523 + str(self.getDomain()) + '.' + str(vif))
525 dev = self.devices[vif]
526 return dev.setCreditLimit(credit, period)
528 def recv_fe_driver_status(self, msg):
529 msg = packMsg('netif_fe_driver_status_t',
530 { 'status' : NETIF_DRIVER_STATUS_UP,
531 ## FIXME: max_handle should be max active interface id
532 'max_handle' : self.getDeviceCount()
533 #'max_handle' : self.getMaxDeviceId()
534 })
535 # Two ways of doing it:
536 # 1) front-end requests driver status, we reply with the interface count,
537 # front-end polls the interfaces,
538 # front-end checks they are all up
539 # 2) front-end requests driver status, we reply (with anything),
540 # we notify the interfaces,
541 # we notify driver status up with the count
542 # front-end checks they are all up
543 #
544 # We really want to use 1), but at the moment the xenU kernel panics
545 # in that mode, so we're sticking to 2) for now.
546 resp = False
547 if resp:
548 self.channel.writeResponse(msg)
549 else:
550 for dev in self.devices.values():
551 dev.reportStatus()
552 self.channel.writeRequest(msg)
553 return resp
555 def recv_fe_interface_status(self, msg):
556 val = unpackMsg('netif_fe_interface_status_t', msg)
557 vif = val['handle']
558 dev = self.findDevice(vif)
559 if dev:
560 dev.reportStatus(resp=True)
561 else:
562 log.error('Received netif_fe_interface_status for unknown vif: dom=%d vif=%d',
563 self.getDomain(), vif)
564 msg = packMsg('netif_fe_interface_status_t',
565 { 'handle' : -1,
566 'status' : NETIF_INTERFACE_STATUS_CLOSED,
567 });
568 self.channel.writeResponse(msg)
569 return True
571 def recv_fe_interface_connect(self, msg):
572 val = unpackMsg('netif_fe_interface_connect_t', msg)
573 vif = val['handle']
574 dev = self.getDevice(vif)
575 if dev:
576 dev.recv_fe_interface_connect(val)
577 else:
578 log.error('Received netif_fe_interface_connect for unknown vif: dom=%d vif=%d',
579 self.getDomain(), vif)