[Top][All Lists]
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[gnue] r6973 - trunk/gnue-common/src/utils
From: |
johannes |
Subject: |
[gnue] r6973 - trunk/gnue-common/src/utils |
Date: |
Mon, 7 Feb 2005 08:52:50 -0600 (CST) |
Author: johannes
Date: 2005-02-07 08:52:49 -0600 (Mon, 07 Feb 2005)
New Revision: 6973
Added:
trunk/gnue-common/src/utils/uuid.py
Log:
Added UUID-Generator
Added: trunk/gnue-common/src/utils/uuid.py
===================================================================
--- trunk/gnue-common/src/utils/uuid.py 2005-02-07 12:28:23 UTC (rev 6972)
+++ trunk/gnue-common/src/utils/uuid.py 2005-02-07 14:52:49 UTC (rev 6973)
@@ -0,0 +1,510 @@
+# GNU Enterprise Common - Utilities - UUID generator
+#
+# Copyright 2001-2005 Free Software Foundation
+#
+# This file is part of GNU Enterprise
+#
+# GNU Enterprise is free software; you can redistribute it
+# and/or modify it under the terms of the GNU General Public
+# License as published by the Free Software Foundation; either
+# version 2, or (at your option) any later version.
+#
+# GNU Enterprise is distributed in the hope that it will be
+# useful, but WITHOUT ANY WARRANTY; without even the implied
+# warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+# PURPOSE. See the GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public
+# License along with program; see the file COPYING. If not,
+# write to the Free Software Foundation, Inc., 59 Temple Place
+# - Suite 330, Boston, MA 02111-1307, USA.
+#
+# $Id$
+
+import sys
+import os
+import string
+import random
+import array
+import threading
+import time
+import types
+import md5
+import sha
+import socket
+import struct
+
+from gnue.common.apps import errors
+
+if os.name == 'nt':
+ try:
+ from win32com.client import GetObject
+ _HAS_GETOBJECT = True
+
+ except ImportError:
+ _HAS_GETOBJECT = False
+
+else:
+ import fcntl
+
+# =============================================================================
+# Exceptions
+# =============================================================================
+
+class InvalidVersionError (errors.SystemError):
+ def __init__ (self, version):
+ msg = u_("The version '%s' is not a valid UUID version") % repr (version)
+ errors.SystemError.__init__ (self, msg)
+
+class InvalidNamespaceError (errors.SystemError):
+ def __init__ (self, ns):
+ msg = u_("'%s' is not recognized as valid namespace argument") % repr (ns)
+ errors.SystemError.__init__ (self, msg)
+
+class MissingNamespaceError (errors.ApplicationError):
+ def __init__ (self):
+ msg = u_("No namespace given for namebased UUID generation")
+ errors.ApplicationError.__init__ (self, msg)
+
+
+# =============================================================================
+# Implementation of a thread safe random number generator
+# =============================================================================
+
+dev_random = None
+pseudoRng = random.Random ()
+lock = threading.Lock ()
+manualLock = sys.version_info [:2] < (2, 3)
+
+if os.name == 'posix':
+ try:
+ dev_random = os.open ('/dev/urandom', os.O_RDONLY)
+
+ except IOError:
+ pass
+
+
+# -----------------------------------------------------------------------------
+# Get a sequence of random bytes
+# -----------------------------------------------------------------------------
+
+def getRandomBytes (count):
+ """
+ This function returns a sequence of 'count' random bytes from the best random
+ number generator available.
+
+ @param count: number of bytes to return
+ @return: sequence of 'count' random bytes
+ """
+
+ result = []
+
+ # If there's a random device we prefer this source
+ if dev_random is not None:
+ lock.acquire ()
+
+ while len (result) < count:
+ rdata = os.read (dev_random, count - len (result))
+ result.extend (array.array ('B', rdata).tolist ())
+
+ lock.release ()
+
+ # otherwise use the random module (which is threadsafe for python 2.3+)
+ else:
+ for i in xrange (count):
+ if manualLock: lock.acquire ()
+ result.append (pseudoRng.randrange (0, 256))
+ if manualLock: lock.release ()
+
+ return result
+
+
+
+# =============================================================================
+# Class implementing an interface to NIC information (hwaddresses)
+# =============================================================================
+
+class NICInformation:
+
+ SIOCGIFCONF = 0x8912 # get interface list
+ SIOCGIFHWADDR = 0x8927 # get hardware address
+
+ # ---------------------------------------------------------------------------
+ # Constructor
+ # ---------------------------------------------------------------------------
+
+ def __init__ (self, autoBuild = True):
+ """
+ This class provides a sequence of network interfaces and their hardware
+ addresses. If a system has no nic installed, an 6-tuple will be generated
+ from random source. This generated address will have the unicast-/multicast
+ bit set.
+
+ @param autoBuild: if set to True a hardware-address will be generated if no
+ network interface card is installed.
+ """
+
+ self.interfaces = self.__getInterfaces ()
+ self.hwaddresses = [self.getHWAddress (i) for i in self.interfaces]
+
+ self.firstMAC = None
+
+ for addr in self.hwaddresses:
+ if not self.__hwAddrIsEmpty (addr):
+ #self.firstMAC = addr
+ break
+
+ if autoBuild and self.firstMAC is None:
+ # Generated mac addresses have the unicast-/multicast-bit set !
+ self.firstMAC = getRandomBytes (6)
+ self.firstMAC [0] |= 0x80
+
+
+ # ---------------------------------------------------------------------------
+ # Get the hardware address of an interface (MAC address)
+ # ---------------------------------------------------------------------------
+
+ def getHWAddress (self, interface):
+ """
+ This function returns the hardware address of an interface as sequence of
+ integers.
+
+ @param interface: network interface to fetch an address for. This is
+ usually an element from the interface-sequence.
+
+ @return: sequence of numbers with the hardware address
+ """
+
+ result = None
+
+ if os.name == 'posix':
+ sfhd = socket.socket (socket.AF_INET, socket.SOCK_DGRAM)
+ ifreq = (interface + '\0' * 32) [:32]
+
+ try:
+ data = fcntl.ioctl (sfhd.fileno (), self.SIOCGIFHWADDR, ifreq)
+ result = array.array ('B', data [18:24]).tolist ()
+
+ finally:
+ sfhd.close ()
+
+ elif os.name == 'nt':
+ parts = interface.MACAddress.split (':')
+ args = [16] * len (parts)
+ result = map (int, parts, args)
+
+ return result
+
+
+ # ---------------------------------------------------------------------------
+ # Get a string representation of a hardware address
+ # ---------------------------------------------------------------------------
+
+ def addrAsString (self, addr):
+ """
+ This function creates a string representation of a hardware address, where
+ all parts are separated by a colon.
+
+ @param addr: hardware address as a sequence of numbers
+ @return: string of the hardware address
+ """
+
+ return string.join (["%02X" % i for i in addr], ":")
+
+
+ # ---------------------------------------------------------------------------
+ # Get a sequence of interfaces available to the system
+ # ---------------------------------------------------------------------------
+
+ def __getInterfaces (self):
+ """
+ This function returns a sequence of network interfaces available to the
+ system. For POSIX systems this is a sequence of strings, and for NT-systems
+ this is a sequence of COM-Objects, representing the nics.
+
+ @return: sequence of network-interfaces
+ """
+
+ result = []
+
+ if os.name == 'posix':
+ sfhd = socket.socket (socket.AF_INET, socket.SOCK_DGRAM)
+
+ try:
+ buffer = array.array ('c', '\0' * 1024)
+ (addr, length) = buffer.buffer_info ()
+ ifconf = struct.pack ("iP", length, addr)
+ data = fcntl.ioctl (sfhd.fileno (), self.SIOCGIFCONF, ifconf)
+
+ size, ptr = struct.unpack ("iP", data)
+ for idx in range (0, size, 32):
+ ifconf = buffer.tostring () [idx:idx+32]
+ name = struct.unpack ("16s16s", ifconf) [0].split ('\0', 1) [0]
+ result.append (name)
+
+ finally:
+ sfhd.close ()
+
+ elif os.name == 'nt' and _HAS_GETOBJECT:
+ result = [i for i in GetObject ('winmgmts:').ExecQuery ("SELECT * FROM "
+ "Win32_NetworkAdapterConfiguration WHERE IPEnabled=True")]
+
+ return result
+
+
+ # ---------------------------------------------------------------------------
+ # Check if a hardware address is NULL (all zeros)
+ # ---------------------------------------------------------------------------
+
+ def __hwAddrIsEmpty (self, address):
+ """
+ This function returns True, if a hardware address is all empty (zero). This
+ is the case for loopback-devices.
+
+ @return: True for empty addresses, False otherwise
+ """
+
+ return address == [0] * len (address)
+
+
+# =============================================================================
+# This class implements an UUID generator
+# =============================================================================
+
+TIME = 1
+MD5 = 3
+RANDOM = 4
+SHA1 = 5
+
+class Generator:
+ """
+ This class implements an UUID generator described in the internet draft at
+ 'http://www.ietf.org/internet-drafts/draft-mealling-uuid-urn-05.txt' or also
+ described in the RPC-RFC.
+ """
+
+ _NIC = NICInformation ()
+
+ # ---------------------------------------------------------------------------
+ # Constructor
+ # ---------------------------------------------------------------------------
+
+ def __init__ (self, version = TIME, namespace = None):
+
+ self.version = version
+ self.__lastTime = None
+ self.__clockSeq = int ("%02x%02x" % tuple (getRandomBytes (2)), 16)
+ self.__clockField = self.__clockSeq & 0x3FFF | 0x8000
+ self.__namespace = None
+
+ if namespace is not None:
+ self.setNamespace (namespace)
+
+ self.__timeFormat = u"%016x%04x%s"
+ self.__randFormat = u"%02x" * 16
+ self.__hashFormat = u"%08x%04x%04x" + "%02x" * 8
+ self.__currNode = "%02x" * 6 % tuple (self._NIC.firstMAC)
+
+
+ # ---------------------------------------------------------------------------
+ # Generate a new UUID
+ # ---------------------------------------------------------------------------
+
+ def generate (self, version = None, namespace = None, name = None):
+ """
+ This function calls the appropriate method to generate a new UUID. If
+ you're about to create a lot of id's in a short time, you're better off
+ using the proper fucntion directly.
+
+ @param version: if set generate an id of this version
+ @param namespace: if version is currently MD5 or SHA1 this parameter
+ defines the namespace to use. Alternatively one can use setNamespace ()
+ @param name: if version is MD5 or SHA1 this parameter specifies the name to
+ encode
+
+ @return: UUID as 32 character unicode string
+ """
+
+ vers = version or self.version
+
+ if vers == TIME:
+ return self.generateTimeBased ()
+
+ elif vers == RANDOM:
+ return self.generateRandom ()
+
+ elif vers == MD5:
+ return self.generateMD5 (name, namespace)
+
+ elif vers == SHA1:
+ return self.generateSHA1 (name, namespace)
+
+ else:
+ raise InvalidVersionError, version
+
+
+ # ---------------------------------------------------------------------------
+ # Generate a time-based UUID
+ # ---------------------------------------------------------------------------
+
+ def generateTimeBased (self):
+ """
+ This function creates a time-based UUID
+
+ @return: UUID (as 32 character unicode string)
+ """
+
+ lock.acquire ()
+
+ timeStamp = long (time.time () * 10000000) + 122192928000000000L
+ if timeStamp != self.__lastTime:
+ self.__uuidsPerTick = 0
+
+ # If the time has been set backward, we change the clock sequence
+ if timeStamp < self.__lastTime:
+ self.__clockSeq += 1
+ if self.__clockSeq > 0xFFFF:
+ self.__clockSeq = 0
+
+ self.__clockField = self.__clockSeq & 0x3FFF | 0x8000
+
+ self.__lastTime = timeStamp
+
+ else:
+ self.__uuidsPerTick += 1
+ timeStamp += self.__uuidsPerTick
+
+ lock.release ()
+
+ timeField = timeStamp & 0x0FFFFFFFFFFFFFFFF | 0x1000000000000000
+
+ return self.__timeFormat % (timeField, self.__clockField, self.__currNode)
+
+
+ # ---------------------------------------------------------------------------
+ # Generate an UUID from pseudo random numbers
+ # ---------------------------------------------------------------------------
+
+ def generateRandom (self):
+ """
+ This function generates an UUID from pseudo random numbers
+
+ @return: UUID (as 32 character unicode string)
+ """
+
+ data = getRandomBytes (16)
+ # Set the two most significant bits (bits 6, 7) of the
+ # clock_seq_hi_and_reserved to zero and one
+ data [8] = data [8] & 0x3F | 0x80
+
+ # Multiplex the most significant bits of time_hi_and_version with 4
+ data [6] = data [6] | 0x40
+
+ return self.__randFormat % tuple (data)
+
+
+ # ---------------------------------------------------------------------------
+ # Generate a name-based UUID using an MD5 hash
+ # ---------------------------------------------------------------------------
+
+ def generateMD5 (self, name, namespace = None):
+ """
+ This function generates a name based UUID using a MD5 hash.
+
+ @return: UUID (as 32 character unicode string)
+ """
+
+ if namespace is not None:
+ self.setNamespace (namespace)
+
+ if self.__namespace is None:
+ raise MissingNamespaceError
+
+ digest = md5.new (self.__namespace)
+ if name is not None:
+ digest.update (name)
+
+ return unicode (digest.hexdigest ())
+
+
+ # ---------------------------------------------------------------------------
+ # Generate an UUID using a SHA1 hash
+ # ---------------------------------------------------------------------------
+
+ def generateSHA1 (self, name, namespace = None):
+ """
+ This function generates a name based UUID using a SHA1 hash.
+
+ @return: UUID (as 32 character unicode string)
+ """
+
+ if namespace is not None:
+ self.setNamespace (namespace)
+
+ if self.__namespace is None:
+ raise MissingNamespaceError
+
+ digest = sha.new (self.__namespace)
+ if name is not None:
+ digest.update (name)
+
+ return unicode (digest.hexdigest () [:32])
+
+
+ # ---------------------------------------------------------------------------
+ # Set the namespace to be used for name based UUIDs
+ # ---------------------------------------------------------------------------
+
+ def setNamespace (self, newNS):
+ """
+ This function sets the namespace used for name based UUID generation
+ """
+
+ if isinstance (newNS, types.StringType) or \
+ isinstance (newNS, types.UnicodeType):
+
+ if len (newNS) == 36:
+ newNS = string.join (newNS.split ('-'), "")
+
+ elif len (newNS) != 32:
+ raise "Invalid namespace argument"
+
+ parts = [newNS [:8], newNS [8:12], newNS [12:16], newNS [16:]]
+
+ timeLow = socket.htonl (long (parts [0], 16))
+ timeMid = socket.htons (int (parts [1], 16))
+ timeHi = socket.htons (int (parts [2], 16))
+ rest = [int (parts [3] [i:i+2], 16) for i in range (0, 16, 2)]
+
+ self.__namespace = struct.pack ('>LHH8B', timeLow, timeMid, timeHi,
*rest)
+
+ else:
+ raise InvalidNamespaceError, newNS
+
+
+# Create a ready-to-use instance of the UUID generator
+UUID = Generator ()
+
+
+# =============================================================================
+# Unit-Test
+# =============================================================================
+
+if __name__ == '__main__':
+ for i in range (5):
+ print "Time-Base:", repr (UUID.generateTimeBased ())
+
+ for i in range (5):
+ print "Random :", repr (UUID.generateRandom ())
+
+ UUID.setNamespace ('6ba7b810-9dad-11d1-80b4-00c04fd430c8')
+ print "Namespace: '6ba7b810-9dad-11d1-80b4-00c04fd430c8'"
+ print "Encoding : 'www.gnuenterprise.org'"
+ print "MD5 :", repr (UUID.generateMD5 ('www.gnuenterprise.org'))
+ print "SHA1 :", repr (UUID.generateSHA1 ('www.gnuenterprise.org'))
+
+
+ print "T:", repr (UUID.generate (version = TIME))
+ print "R:", repr (UUID.generate (version = RANDOM))
+ print "M:", repr (UUID.generate (version = MD5, name = 'foobar'))
+ print "S:", repr (UUID.generate (version = SHA1, name = 'foobar'))
Property changes on: trunk/gnue-common/src/utils/uuid.py
___________________________________________________________________
Name: svn:keywords
+ Id
[Prev in Thread] |
Current Thread |
[Next in Thread] |
- [gnue] r6973 - trunk/gnue-common/src/utils,
johannes <=