commit-gnue
[Top][All Lists]
Advanced

[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





reply via email to

[Prev in Thread] Current Thread [Next in Thread]