summaryrefslogtreecommitdiffstats
path: root/nova/utils.py
diff options
context:
space:
mode:
authorDan Prince <dan.prince@rackspace.com>2011-05-26 09:05:14 -0400
committerDan Prince <dan.prince@rackspace.com>2011-05-26 09:05:14 -0400
commita021b43b7d45f8cc803721002a0b2e7d9ea9a06d (patch)
tree6c3f6274d91a511a04a322a46d8678f4ad18d71a /nova/utils.py
parent9d2513ea3a6d586e1fe3deae778a02bb089b9a5e (diff)
parentdb18a792414240cbdb1221d0e79e8a63313f103e (diff)
downloadnova-a021b43b7d45f8cc803721002a0b2e7d9ea9a06d.tar.gz
nova-a021b43b7d45f8cc803721002a0b2e7d9ea9a06d.tar.xz
nova-a021b43b7d45f8cc803721002a0b2e7d9ea9a06d.zip
Merge w/ trunk.
Diffstat (limited to 'nova/utils.py')
-rw-r--r--nova/utils.py204
1 files changed, 107 insertions, 97 deletions
diff --git a/nova/utils.py b/nova/utils.py
index 3f6f9fc8a..361fc9873 100644
--- a/nova/utils.py
+++ b/nova/utils.py
@@ -17,9 +17,7 @@
# License for the specific language governing permissions and limitations
# under the License.
-"""
-System-level utilities and helper functions.
-"""
+"""Utilities and helper functions."""
import base64
import datetime
@@ -43,9 +41,8 @@ from eventlet import event
from eventlet import greenthread
from eventlet import semaphore
from eventlet.green import subprocess
-None
+
from nova import exception
-from nova.exception import ProcessExecutionError
from nova import flags
from nova import log as logging
@@ -56,18 +53,18 @@ FLAGS = flags.FLAGS
def import_class(import_str):
- """Returns a class from a string including module and class"""
+ """Returns a class from a string including module and class."""
mod_str, _sep, class_str = import_str.rpartition('.')
try:
__import__(mod_str)
return getattr(sys.modules[mod_str], class_str)
except (ImportError, ValueError, AttributeError), exc:
LOG.debug(_('Inner Exception: %s'), exc)
- raise exception.NotFound(_('Class %s cannot be found') % class_str)
+ raise exception.ClassNotFound(class_name=class_str)
def import_object(import_str):
- """Returns an object including a module or module and class"""
+ """Returns an object including a module or module and class."""
try:
__import__(import_str)
return sys.modules[import_str]
@@ -99,11 +96,12 @@ def vpn_ping(address, port, timeout=0.05, session_id=None):
cli_id = 64 bit identifier
? = unknown, probably flags/padding
bit 9 was 1 and the rest were 0 in testing
+
"""
if session_id is None:
session_id = random.randint(0, 0xffffffffffffffff)
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
- data = struct.pack("!BQxxxxxx", 0x38, session_id)
+ data = struct.pack('!BQxxxxxx', 0x38, session_id)
sock.sendto(data, (address, port))
sock.settimeout(timeout)
try:
@@ -112,7 +110,7 @@ def vpn_ping(address, port, timeout=0.05, session_id=None):
return False
finally:
sock.close()
- fmt = "!BQxxxxxQxxxx"
+ fmt = '!BQxxxxxQxxxx'
if len(received) != struct.calcsize(fmt):
print struct.calcsize(fmt)
return False
@@ -122,15 +120,8 @@ def vpn_ping(address, port, timeout=0.05, session_id=None):
def fetchfile(url, target):
- LOG.debug(_("Fetching %s") % url)
-# c = pycurl.Curl()
-# fp = open(target, "wb")
-# c.setopt(c.URL, url)
-# c.setopt(c.WRITEDATA, fp)
-# c.perform()
-# c.close()
-# fp.close()
- execute("curl", "--fail", url, "-o", target)
+ LOG.debug(_('Fetching %s') % url)
+ execute('curl', '--fail', url, '-o', target)
def execute(*cmd, **kwargs):
@@ -147,7 +138,7 @@ def execute(*cmd, **kwargs):
while attempts > 0:
attempts -= 1
try:
- LOG.debug(_("Running cmd (subprocess): %s"), ' '.join(cmd))
+ LOG.debug(_('Running cmd (subprocess): %s'), ' '.join(cmd))
env = os.environ.copy()
if addl_env:
env.update(addl_env)
@@ -157,26 +148,27 @@ def execute(*cmd, **kwargs):
stderr=subprocess.PIPE,
env=env)
result = None
- if process_input != None:
+ if process_input is not None:
result = obj.communicate(process_input)
else:
result = obj.communicate()
obj.stdin.close()
if obj.returncode:
- LOG.debug(_("Result was %s") % obj.returncode)
+ LOG.debug(_('Result was %s') % obj.returncode)
if type(check_exit_code) == types.IntType \
and obj.returncode != check_exit_code:
(stdout, stderr) = result
- raise ProcessExecutionError(exit_code=obj.returncode,
- stdout=stdout,
- stderr=stderr,
- cmd=' '.join(cmd))
+ raise exception.ProcessExecutionError(
+ exit_code=obj.returncode,
+ stdout=stdout,
+ stderr=stderr,
+ cmd=' '.join(cmd))
return result
- except ProcessExecutionError:
+ except exception.ProcessExecutionError:
if not attempts:
raise
else:
- LOG.debug(_("%r failed. Retrying."), cmd)
+ LOG.debug(_('%r failed. Retrying.'), cmd)
if delay_on_retry:
greenthread.sleep(random.randint(20, 200) / 100.0)
finally:
@@ -188,13 +180,13 @@ def execute(*cmd, **kwargs):
def ssh_execute(ssh, cmd, process_input=None,
addl_env=None, check_exit_code=True):
- LOG.debug(_("Running cmd (SSH): %s"), ' '.join(cmd))
+ LOG.debug(_('Running cmd (SSH): %s'), ' '.join(cmd))
if addl_env:
- raise exception.Error("Environment not supported over SSH")
+ raise exception.Error(_('Environment not supported over SSH'))
if process_input:
# This is (probably) fixable if we need it...
- raise exception.Error("process_input not supported over SSH")
+ raise exception.Error(_('process_input not supported over SSH'))
stdin_stream, stdout_stream, stderr_stream = ssh.exec_command(cmd)
channel = stdout_stream.channel
@@ -212,7 +204,7 @@ def ssh_execute(ssh, cmd, process_input=None,
# exit_status == -1 if no exit code was returned
if exit_status != -1:
- LOG.debug(_("Result was %s") % exit_status)
+ LOG.debug(_('Result was %s') % exit_status)
if check_exit_code and exit_status != 0:
raise exception.ProcessExecutionError(exit_code=exit_status,
stdout=stdout,
@@ -240,9 +232,12 @@ def default_flagfile(filename='nova.conf'):
# turn relative filename into an absolute path
script_dir = os.path.dirname(inspect.stack()[-1][1])
filename = os.path.abspath(os.path.join(script_dir, filename))
- if os.path.exists(filename):
- flagfile = ['--flagfile=%s' % filename]
- sys.argv = sys.argv[:1] + flagfile + sys.argv[1:]
+ if not os.path.exists(filename):
+ filename = "./nova.conf"
+ if not os.path.exists(filename):
+ filename = '/etc/nova/nova.conf'
+ flagfile = ['--flagfile=%s' % filename]
+ sys.argv = sys.argv[:1] + flagfile + sys.argv[1:]
def debug(arg):
@@ -251,7 +246,7 @@ def debug(arg):
def runthis(prompt, *cmd, **kwargs):
- LOG.debug(_("Running %s"), (" ".join(cmd)))
+ LOG.debug(_('Running %s'), (' '.join(cmd)))
rv, err = execute(*cmd, **kwargs)
@@ -266,68 +261,49 @@ def generate_mac():
random.randint(0x00, 0x7f),
random.randint(0x00, 0xff),
random.randint(0x00, 0xff)]
- return ':'.join(map(lambda x: "%02x" % x, mac))
+ return ':'.join(map(lambda x: '%02x' % x, mac))
# Default symbols to use for passwords. Avoids visually confusing characters.
# ~6 bits per symbol
-DEFAULT_PASSWORD_SYMBOLS = ("23456789" # Removed: 0,1
- "ABCDEFGHJKLMNPQRSTUVWXYZ" # Removed: I, O
- "abcdefghijkmnopqrstuvwxyz") # Removed: l
+DEFAULT_PASSWORD_SYMBOLS = ('23456789' # Removed: 0,1
+ 'ABCDEFGHJKLMNPQRSTUVWXYZ' # Removed: I, O
+ 'abcdefghijkmnopqrstuvwxyz') # Removed: l
# ~5 bits per symbol
-EASIER_PASSWORD_SYMBOLS = ("23456789" # Removed: 0, 1
- "ABCDEFGHJKLMNPQRSTUVWXYZ") # Removed: I, O
+EASIER_PASSWORD_SYMBOLS = ('23456789' # Removed: 0, 1
+ 'ABCDEFGHJKLMNPQRSTUVWXYZ') # Removed: I, O
def generate_password(length=20, symbols=DEFAULT_PASSWORD_SYMBOLS):
"""Generate a random password from the supplied symbols.
Believed to be reasonably secure (with a reasonable password length!)
+
"""
r = random.SystemRandom()
- return "".join([r.choice(symbols) for _i in xrange(length)])
+ return ''.join([r.choice(symbols) for _i in xrange(length)])
def last_octet(address):
- return int(address.split(".")[-1])
+ return int(address.split('.')[-1])
def get_my_linklocal(interface):
try:
- if_str = execute("ip", "-f", "inet6", "-o", "addr", "show", interface)
- condition = "\s+inet6\s+([0-9a-f:]+)/\d+\s+scope\s+link"
+ if_str = execute('ip', '-f', 'inet6', '-o', 'addr', 'show', interface)
+ condition = '\s+inet6\s+([0-9a-f:]+)/\d+\s+scope\s+link'
links = [re.search(condition, x) for x in if_str[0].split('\n')]
address = [w.group(1) for w in links if w is not None]
if address[0] is not None:
return address[0]
else:
- raise exception.Error(_("Link Local address is not found.:%s")
+ raise exception.Error(_('Link Local address is not found.:%s')
% if_str)
except Exception as ex:
raise exception.Error(_("Couldn't get Link Local IP of %(interface)s"
- " :%(ex)s") % locals())
-
-
-def to_global_ipv6(prefix, mac):
- try:
- mac64 = netaddr.EUI(mac).eui64().words
- int_addr = int(''.join(['%02x' % i for i in mac64]), 16)
- mac64_addr = netaddr.IPAddress(int_addr)
- maskIP = netaddr.IPNetwork(prefix).ip
- return (mac64_addr ^ netaddr.IPAddress('::0200:0:0:0') | maskIP).\
- format()
- except TypeError:
- raise TypeError(_("Bad mac for to_global_ipv6: %s") % mac)
-
-
-def to_mac(ipv6_address):
- address = netaddr.IPAddress(ipv6_address)
- mask1 = netaddr.IPAddress("::ffff:ffff:ffff:ffff")
- mask2 = netaddr.IPAddress("::0200:0:0:0")
- mac64 = netaddr.EUI(int(address & mask1 ^ mask2)).words
- return ":".join(["%02x" % i for i in mac64[0:3] + mac64[5:8]])
+ " :%(ex)s") % locals())
def utcnow():
@@ -341,7 +317,7 @@ utcnow.override_time = None
def is_older_than(before, seconds):
- """Return True if before is older than seconds"""
+ """Return True if before is older than seconds."""
return utcnow() - before > datetime.timedelta(seconds=seconds)
@@ -379,7 +355,7 @@ def isotime(at=None):
def parse_isotime(timestr):
- """Turn an iso formatted time back into a datetime"""
+ """Turn an iso formatted time back into a datetime."""
return datetime.datetime.strptime(timestr, TIME_FORMAT)
@@ -433,16 +409,19 @@ class LazyPluggable(object):
class LoopingCallDone(Exception):
- """The poll-function passed to LoopingCall can raise this exception to
+ """Exception to break out and stop a LoopingCall.
+
+ The poll-function passed to LoopingCall can raise this exception to
break out of the loop normally. This is somewhat analogous to
StopIteration.
An optional return-value can be included as the argument to the exception;
this return-value will be returned by LoopingCall.wait()
+
"""
def __init__(self, retvalue=True):
- """:param retvalue: Value that LoopingCall.wait() should return"""
+ """:param retvalue: Value that LoopingCall.wait() should return."""
self.retvalue = retvalue
@@ -463,6 +442,8 @@ class LoopingCall(object):
try:
while self._running:
self.f(*self.args, **self.kw)
+ if not self._running:
+ break
greenthread.sleep(interval)
except LoopingCallDone, e:
self.stop()
@@ -493,7 +474,7 @@ def xhtml_escape(value):
http://github.com/facebook/tornado/blob/master/tornado/escape.py
"""
- return saxutils.escape(value, {'"': "&quot;"})
+ return saxutils.escape(value, {'"': '&quot;'})
def utf8(value):
@@ -504,7 +485,7 @@ def utf8(value):
"""
if isinstance(value, unicode):
- return value.encode("utf-8")
+ return value.encode('utf-8')
assert isinstance(value, str)
return value
@@ -554,7 +535,7 @@ class _NoopContextManager(object):
def synchronized(name, external=False):
- """Synchronization decorator
+ """Synchronization decorator.
Decorating a method like so:
@synchronized('mylock')
@@ -578,6 +559,7 @@ def synchronized(name, external=False):
multiple processes. This means that if two different workers both run a
a method decorated with @synchronized('mylock', external=True), only one
of them will execute at a time.
+
"""
def wrap(f):
@@ -590,13 +572,13 @@ def synchronized(name, external=False):
_semaphores[name] = semaphore.Semaphore()
sem = _semaphores[name]
LOG.debug(_('Attempting to grab semaphore "%(lock)s" for method '
- '"%(method)s"...' % {"lock": name,
- "method": f.__name__}))
+ '"%(method)s"...' % {'lock': name,
+ 'method': f.__name__}))
with sem:
if external:
LOG.debug(_('Attempting to grab file lock "%(lock)s" for '
'method "%(method)s"...' %
- {"lock": name, "method": f.__name__}))
+ {'lock': name, 'method': f.__name__}))
lock_file_path = os.path.join(FLAGS.lock_path,
'nova-%s.lock' % name)
lock = lockfile.FileLock(lock_file_path)
@@ -617,21 +599,23 @@ def synchronized(name, external=False):
def get_from_path(items, path):
- """ Returns a list of items matching the specified path. Takes an
- XPath-like expression e.g. prop1/prop2/prop3, and for each item in items,
- looks up items[prop1][prop2][prop3]. Like XPath, if any of the
+ """Returns a list of items matching the specified path.
+
+ Takes an XPath-like expression e.g. prop1/prop2/prop3, and for each item
+ in items, looks up items[prop1][prop2][prop3]. Like XPath, if any of the
intermediate results are lists it will treat each list item individually.
A 'None' in items or any child expressions will be ignored, this function
will not throw because of None (anywhere) in items. The returned list
- will contain no None values."""
+ will contain no None values.
+ """
if path is None:
- raise exception.Error("Invalid mini_xpath")
+ raise exception.Error('Invalid mini_xpath')
- (first_token, sep, remainder) = path.partition("/")
+ (first_token, sep, remainder) = path.partition('/')
- if first_token == "":
- raise exception.Error("Invalid mini_xpath")
+ if first_token == '':
+ raise exception.Error('Invalid mini_xpath')
results = []
@@ -645,7 +629,7 @@ def get_from_path(items, path):
for item in items:
if item is None:
continue
- get_method = getattr(item, "get", None)
+ get_method = getattr(item, 'get', None)
if get_method is None:
continue
child = get_method(first_token)
@@ -666,7 +650,7 @@ def get_from_path(items, path):
def flatten_dict(dict_, flattened=None):
- """Recursively flatten a nested dictionary"""
+ """Recursively flatten a nested dictionary."""
flattened = flattened or {}
for key, value in dict_.iteritems():
if hasattr(value, 'iteritems'):
@@ -677,9 +661,7 @@ def flatten_dict(dict_, flattened=None):
def partition_dict(dict_, keys):
- """Return two dicts, one containing only `keys` the other containing
- everything but `keys`
- """
+ """Return two dicts, one with `keys` the other with everything else."""
intersection = {}
difference = {}
for key, value in dict_.iteritems():
@@ -691,9 +673,7 @@ def partition_dict(dict_, keys):
def map_dict_keys(dict_, key_map):
- """Return a dictionary in which the dictionaries keys are mapped to
- new keys.
- """
+ """Return a dict in which the dictionaries keys are mapped to new keys."""
mapped = {}
for key, value in dict_.iteritems():
mapped_key = key_map[key] if key in key_map else key
@@ -702,15 +682,45 @@ def map_dict_keys(dict_, key_map):
def subset_dict(dict_, keys):
- """Return a dict that only contains a subset of keys"""
+ """Return a dict that only contains a subset of keys."""
subset = partition_dict(dict_, keys)[0]
return subset
def check_isinstance(obj, cls):
- """Checks that obj is of type cls, and lets PyLint infer types"""
+ """Checks that obj is of type cls, and lets PyLint infer types."""
if isinstance(obj, cls):
return obj
- raise Exception(_("Expected object of type: %s") % (str(cls)))
+ raise Exception(_('Expected object of type: %s') % (str(cls)))
# TODO(justinsb): Can we make this better??
return cls() # Ugly PyLint hack
+
+
+def parse_server_string(server_str):
+ """
+ Parses the given server_string and returns a list of host and port.
+ If it's not a combination of host part and port, the port element
+ is a null string. If the input is invalid expression, return a null
+ list.
+ """
+ try:
+ # First of all, exclude pure IPv6 address (w/o port).
+ if netaddr.valid_ipv6(server_str):
+ return (server_str, '')
+
+ # Next, check if this is IPv6 address with a port number combination.
+ if server_str.find("]:") != -1:
+ (address, port) = server_str.replace('[', '', 1).split(']:')
+ return (address, port)
+
+ # Third, check if this is a combination of an address and a port
+ if server_str.find(':') == -1:
+ return (server_str, '')
+
+ # This must be a combination of an address and a port
+ (address, port) = server_str.split(':')
+ return (address, port)
+
+ except:
+ LOG.debug(_('Invalid server_string: %s' % server_str))
+ return ('', '')