source: trunk/packages/xen-3.1/xen-3.1/tools/python/xen/xend/XendPIF.py @ 34

Last change on this file since 34 was 34, checked in by hartmans, 18 years ago

Add xen and xen-common

File size: 11.0 KB
Line 
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) 2006 Xensource Inc.
16#============================================================================
17
18import commands
19import logging
20import os
21import re
22from xen.xend import uuid as genuuid
23from xen.xend import XendAPIStore
24from xen.xend.XendBase import XendBase
25from xen.xend.XendPIFMetrics import XendPIFMetrics
26from xen.xend.XendError import *
27
28log = logging.getLogger("xend.XendPIF")
29log.setLevel(logging.TRACE)
30
31MAC_RE = re.compile(':'.join(['[0-9a-f]{2}'] * 6))
32IP_IFACE_RE = re.compile(r'^\d+: (\w+):.*mtu (\d+) .* link/\w+ ([0-9a-f:]+)')
33
34def linux_phy_to_virt(pif_name):
35    return 'eth' + re.sub(r'^[a-z]+', '', pif_name)
36
37def linux_get_phy_ifaces():
38    """Returns a list of physical interfaces.
39
40    Identifies PIFs as those that have a interface name starting with 'p'
41    and have the fake 'fe:ff:ff:ff:ff:ff' MAC address.
42
43    See /etc/xen/scripts/network-bridge for how the devices are renamed.
44
45    @rtype: array of 3-element tuple (name, mtu, mac)
46    """
47   
48    ip_cmd = 'ip -o link show'
49    rc, output = commands.getstatusoutput(ip_cmd)
50    ifaces = {}
51    phy_ifaces = []
52    if rc == 0:
53        # parse all interfaces into (name, mtu, mac)
54        for line in output.split('\n'):
55            has_if = re.search(IP_IFACE_RE, line)
56            if has_if:
57                ifaces[has_if.group(1)] = has_if.groups()
58               
59        # resolve pifs' mac addresses
60        for name, mtu, mac in ifaces.values():
61            if name[0] == 'p' and mac == 'fe:ff:ff:ff:ff:ff':
62                bridged_ifname = linux_phy_to_virt(name)
63                bridged_if = ifaces.get(bridged_ifname)
64                if bridged_if:
65                    bridged_mac = bridged_if[2]
66                phy_ifaces.append((name, int(mtu), bridged_mac))
67               
68    return phy_ifaces
69
70def linux_set_mac(iface, mac):
71    if not re.search(MAC_RE, mac):
72        return False
73
74    ip_mac_cmd = 'ip link set %s addr %s' % \
75                 (linux_phy_to_virt(iface), mac)
76    rc, output = commands.getstatusoutput(ip_mac_cmd)
77    if rc == 0:
78        return True
79
80    return False
81
82def linux_set_mtu(iface, mtu):
83    try:
84        ip_mtu_cmd = 'ip link set %s mtu %d' % \
85                     (linux_phy_to_virt(iface), int(mtu))
86        rc, output = commands.getstatusoutput(ip_mtu_cmd)
87        if rc == 0:
88            return True
89        return False
90    except ValueError:
91        return False
92
93def _create_VLAN(dev, vlan):
94    rc, _ = commands.getstatusoutput('vconfig add %s %d' %
95                                     (dev, vlan))
96    if rc != 0:
97        return False
98
99    rc, _ = commands.getstatusoutput('ifconfig %s.%d up' %
100                                     (dev, vlan))
101    return rc == 0
102
103def _destroy_VLAN(dev, vlan):
104    rc, _ = commands.getstatusoutput('ifconfig %s.%d down' %
105                                     (dev, vlan))
106    if rc != 0:
107        return False
108                                     
109    rc, _ = commands.getstatusoutput('vconfig rem %s.%d' %
110                                     (dev, vlan))
111    return rc == 0
112
113class XendPIF(XendBase):
114    """Representation of a Physical Network Interface."""
115
116    def getClass(self):
117        return "PIF"
118
119    def getAttrRO(self):
120        attrRO = ['network',
121                  'host',
122                  'metrics',
123                  'device',
124                  'VLAN']
125        return XendBase.getAttrRO() + attrRO
126   
127    def getAttrRW(self):
128        attrRW = ['MAC',
129                  'MTU']
130        return XendBase.getAttrRW() + attrRW
131
132    def getAttrInst(self):
133        attrInst = ['network',
134                    'device',
135                    'MAC',
136                    'MTU',
137                    'VLAN']
138        return attrInst
139
140    def getMethods(self):
141        methods = ['plug',
142                   'unplug',
143                   'destroy']
144        return XendBase.getMethods() + methods
145
146    def getFuncs(self):
147        funcs = ['create_VLAN']
148        return XendBase.getFuncs() + funcs
149
150    getClass    = classmethod(getClass)
151    getAttrRO   = classmethod(getAttrRO)
152    getAttrRW   = classmethod(getAttrRW)
153    getAttrInst = classmethod(getAttrInst)
154    getMethods  = classmethod(getMethods)
155    getFuncs    = classmethod(getFuncs)
156   
157    def create_phy(self, network_uuid, device,
158                   MAC, MTU):
159        """
160        Called when a new physical PIF is found
161        Could be a VLAN...
162        """
163        # Create new uuids
164        pif_uuid = genuuid.createString()
165        metrics_uuid = genuuid.createString()
166
167        # Create instances
168        metrics = XendPIFMetrics(metrics_uuid, pif_uuid)
169
170        # Is this a VLAN?
171        VLANdot = device.split(".")
172        VLANcolon = device.split(":")
173
174        if len(VLANdot) > 1:
175            VLAN = VLANdot[1]
176            device = VLANdot[0]
177        elif len(VLANcolon) > 1:
178            VLAN = VLANcolon[1]
179            device = VLANcolon[0] 
180        else:
181            VLAN = -1
182           
183        record = {
184            'network': network_uuid,
185            'device':  device,
186            'MAC':     MAC,
187            'MTU':     MTU,
188            'VLAN':    VLAN
189            }
190        pif = XendPIF(record, pif_uuid, metrics_uuid)
191
192        return pif_uuid
193
194    def recreate(self, record, uuid):
195        """Called on xend start / restart"""       
196        pif_uuid = uuid
197        metrics_uuid = record['metrics']
198
199        # Create instances
200        metrics = XendPIFMetrics(metrics_uuid, pif_uuid)
201        pif = XendPIF(record, pif_uuid, metrics_uuid)
202
203        # If physical PIF, check exists
204        # If VLAN, create if not exist
205        ifs = [dev for dev, _1, _2 in linux_get_phy_ifaces()]
206        if pif.get_VLAN() == -1:
207            if pif.get_device() not in ifs:
208                XendBase.destroy(pif)
209                metrics.destroy()
210                return None
211        else:
212            if pif.get_interface_name() not in ifs:
213                _create_VLAN(pif.get_device(), pif.get_VLAN())
214                pif.plug()
215
216        return pif_uuid
217
218    def create_VLAN(self, device, network_uuid, host_ref, vlan):
219        """Exposed via API - create a new VLAN from existing VIF"""
220       
221        ifs = [name for name, _, _ in linux_get_phy_ifaces()]
222
223        vlan = int(vlan)
224
225        # Check VLAN tag is valid
226        if vlan < 0 or vlan >= 4096:
227            raise VLANTagInvalid(vlan)
228       
229        # Check device exists
230        if device not in ifs:
231            raise InvalidDeviceError(device)
232
233        # Check VLAN doesn't already exist
234        if "%s.%d" % (device, vlan) in ifs:
235            raise DeviceExistsError("%s.%d" % (device, vlan))
236
237        # Check network ref is valid
238        from XendNetwork import XendNetwork
239        if network_uuid not in XendNetwork.get_all():
240            raise InvalidHandleError("Network", network_uuid)
241
242        # Check host_ref is this host
243        import XendNode
244        if host_ref != XendNode.instance().get_uuid():
245            raise InvalidHandleError("Host", host_ref)
246
247        # Create the VLAN
248        _create_VLAN(device, vlan)
249
250        # Create new uuids
251        pif_uuid = genuuid.createString()
252        metrics_uuid = genuuid.createString()
253
254        # Create the record
255        record = {
256            "device":  device,
257            "MAC":     '',
258            "MTU":     '',
259            "network": network_uuid,
260            "VLAN":    vlan
261            }
262
263        # Create instances
264        metrics = XendPIFMetrics(metrics_uuid, pif_uuid)
265        pif = XendPIF(record, pif_uuid, metrics_uuid)
266
267        # Not sure if they should be created plugged or not...
268        pif.plug()
269
270        XendNode.instance().save_PIFs()
271        return pif_uuid
272
273    create_phy  = classmethod(create_phy)
274    recreate    = classmethod(recreate)
275    create_VLAN = classmethod(create_VLAN)
276   
277    def __init__(self, record, uuid, metrics_uuid):
278        XendBase.__init__(self, uuid, record)
279        self.metrics = metrics_uuid
280
281    def plug(self):
282        """Plug the PIF into the network"""
283        network = XendAPIStore.get(self.network,
284                                   "network")
285        bridge_name = network.get_name_label()
286
287        from xen.util import Brctl
288        Brctl.vif_bridge_add({
289            "bridge": bridge_name,
290            "vif":    self.get_interface_name()
291            })
292
293    def unplug(self):
294        """Unplug the PIF from the network"""
295        network = XendAPIStore.get(self.network,
296                                   "network")
297        bridge_name = network.get_name_label()
298
299        from xen.util import Brctl
300        Brctl.vif_bridge_rem({
301            "bridge": bridge_name,
302            "vif":    self.get_interface_name()
303            })
304
305    def destroy(self):
306        # Figure out if this is a physical device
307        if self.get_interface_name() == \
308           self.get_device():
309            raise PIFIsPhysical()
310
311        self.unplug()
312
313        if _destroy_VLAN(self.get_device(), self.get_VLAN()):
314            XendBase.destroy(self)
315            import XendNode
316            XendNode.instance().save_PIFs()
317        else:
318            raise NetworkError("Unable to delete VLAN", self.get_uuid())
319
320    def get_interface_name(self):
321        if self.get_VLAN() == -1:
322            return self.get_device()
323        else:
324            return "%s.%d" % (self.get_device(), self.get_VLAN())
325       
326    def get_device(self):
327        """
328        This is the base interface.
329        For phy if (VLAN == -1) this is same as
330        if name.
331        For VLANs, this it the bit before the period
332        """
333        return self.device
334
335    def get_network(self):
336        return self.network
337
338    def get_host(self):
339        from xen.xend import XendNode
340        return XendNode.instance().get_uuid()
341
342    def get_metrics(self):
343        return self.metrics
344
345    def get_MAC(self):
346        return self.MAC
347
348    def set_MAC(self, new_mac):
349        success = linux_set_mac(self.device, new_mac)
350        if success:
351            self.MAC = new_mac
352            import XendNode
353            XendNode.instance().save_PIFs()
354        return success
355
356    def get_MTU(self):
357        return self.MTU
358
359    def set_MTU(self, new_mtu):
360        success = linux_set_mtu(self.device, new_mtu)
361        if success:
362            self.MTU = new_mtu
363            import XendNode
364            XendNode.instance().save_PIFs()
365        return success
366
367    def get_VLAN(self):
368        return self.VLAN
Note: See TracBrowser for help on using the repository browser.