From 5812a95736b9a16733b99700e8664dd29ae34def Mon Sep 17 00:00:00 2001 From: Soren Hansen Date: Fri, 18 Feb 2011 22:10:06 +0100 Subject: Introduce IptablesManager in linux_net. Port every use of iptables in linux_net to it. --- nova/utils.py | 61 ++++++++++++++++++++++++++++++++++------------------------- 1 file changed, 35 insertions(+), 26 deletions(-) (limited to 'nova/utils.py') diff --git a/nova/utils.py b/nova/utils.py index ba71ebf39..bf3a4b098 100644 --- a/nova/utils.py +++ b/nova/utils.py @@ -124,32 +124,41 @@ def fetchfile(url, target): execute("curl --fail %s -o %s" % (url, target)) -def execute(cmd, process_input=None, addl_env=None, check_exit_code=True): - LOG.debug(_("Running cmd (subprocess): %s"), cmd) - env = os.environ.copy() - if addl_env: - env.update(addl_env) - obj = subprocess.Popen(cmd, shell=True, stdin=subprocess.PIPE, - stdout=subprocess.PIPE, stderr=subprocess.PIPE, env=env) - result = None - if process_input != None: - result = obj.communicate(process_input) - else: - result = obj.communicate() - obj.stdin.close() - if obj.returncode: - LOG.debug(_("Result was %s") % obj.returncode) - if check_exit_code and obj.returncode != 0: - (stdout, stderr) = result - raise ProcessExecutionError(exit_code=obj.returncode, - stdout=stdout, - stderr=stderr, - cmd=cmd) - # NOTE(termie): this appears to be necessary to let the subprocess call - # clean something up in between calls, without it two - # execute calls in a row hangs the second one - greenthread.sleep(0) - return result +def execute(cmd, process_input=None, addl_env=None, check_exit_code=True, attempts=1): + while attempts > 0: + attempts -= 1 + try: + LOG.debug(_("Running cmd (subprocess): %s"), cmd) + env = os.environ.copy() + if addl_env: + env.update(addl_env) + obj = subprocess.Popen(cmd, shell=True, stdin=subprocess.PIPE, + stdout=subprocess.PIPE, stderr=subprocess.PIPE, env=env) + result = None + if process_input != None: + result = obj.communicate(process_input) + else: + result = obj.communicate() + obj.stdin.close() + if obj.returncode: + LOG.debug(_("Result was %s") % obj.returncode) + if check_exit_code and obj.returncode != 0: + (stdout, stderr) = result + raise ProcessExecutionError(exit_code=obj.returncode, + stdout=stdout, + stderr=stderr, + cmd=cmd) + # NOTE(termie): this appears to be necessary to let the subprocess call + # clean something up in between calls, without it two + # execute calls in a row hangs the second one + greenthread.sleep(0) + return result + except ProcessExecutionError: + if not attempts: + raise + else: + greenthread.sleep(random.randint(50,300)/100) + pass def ssh_execute(ssh, cmd, process_input=None, -- cgit From a57dffb5fdfbfac59b9ddbe7b33d6f03b7b748ba Mon Sep 17 00:00:00 2001 From: Soren Hansen Date: Mon, 21 Feb 2011 14:16:42 +0100 Subject: PEP-8 fixes --- nova/utils.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) (limited to 'nova/utils.py') diff --git a/nova/utils.py b/nova/utils.py index 644bf18fd..5b44bccb5 100644 --- a/nova/utils.py +++ b/nova/utils.py @@ -126,7 +126,8 @@ def fetchfile(url, target): execute("curl --fail %s -o %s" % (url, target)) -def execute(cmd, process_input=None, addl_env=None, check_exit_code=True, attempts=1): +def execute(cmd, process_input=None, addl_env=None, check_exit_code=True, + attempts=1): while attempts > 0: attempts -= 1 try: @@ -150,17 +151,16 @@ def execute(cmd, process_input=None, addl_env=None, check_exit_code=True, attemp stdout=stdout, stderr=stderr, cmd=cmd) - # NOTE(termie): this appears to be necessary to let the subprocess call - # clean something up in between calls, without it two - # execute calls in a row hangs the second one + # NOTE(termie): this appears to be necessary to let the subprocess + # call clean something up in between calls, without + # it two execute calls in a row hangs the second one greenthread.sleep(0) return result except ProcessExecutionError: if not attempts: raise else: - greenthread.sleep(random.randint(50,300)/100) - pass + greenthread.sleep(random.randint(20, 200) / 100.0) def ssh_execute(ssh, cmd, process_input=None, -- cgit From 38c21546ecc079300c575e5950bcb990eecee3a3 Mon Sep 17 00:00:00 2001 From: Eric Windisch Date: Sun, 27 Feb 2011 20:28:04 -0500 Subject: execute: shell=True removed. --- nova/utils.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'nova/utils.py') diff --git a/nova/utils.py b/nova/utils.py index 0cf91e0cc..40a8d8d8c 100644 --- a/nova/utils.py +++ b/nova/utils.py @@ -125,7 +125,7 @@ def fetchfile(url, target): # c.perform() # c.close() # fp.close() - execute("curl --fail %s -o %s" % (url, target)) + execute("curl","--fail",url,"-o",target) def execute(cmd, process_input=None, addl_env=None, check_exit_code=True): @@ -133,7 +133,7 @@ def execute(cmd, process_input=None, addl_env=None, check_exit_code=True): env = os.environ.copy() if addl_env: env.update(addl_env) - obj = subprocess.Popen(cmd, shell=True, stdin=subprocess.PIPE, + obj = subprocess.Popen(cmd, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, env=env) result = None if process_input != None: @@ -254,7 +254,7 @@ def last_octet(address): def get_my_linklocal(interface): try: - if_str = execute("ip -f inet6 -o addr show %s" % interface) + 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] -- cgit From 8b3e9ad11c2f5c425701f1eb4abb7b3f577ae1cc Mon Sep 17 00:00:00 2001 From: Soren Hansen Date: Mon, 28 Feb 2011 12:37:02 +0100 Subject: Add utils.synchronized decorator to allow for synchronising method entrance across multiple workers on the same host. --- nova/utils.py | 11 +++++++++++ 1 file changed, 11 insertions(+) (limited to 'nova/utils.py') diff --git a/nova/utils.py b/nova/utils.py index 0cf91e0cc..cb1ea5a7d 100644 --- a/nova/utils.py +++ b/nova/utils.py @@ -25,6 +25,7 @@ import base64 import datetime import inspect import json +import lockfile import os import random import socket @@ -491,6 +492,16 @@ def loads(s): return json.loads(s) +def synchronized(name): + def wrap(f): + def inner(*args, **kwargs): + lock = lockfile.FileLock('nova-%s.lock' % name) + with lock: + return f(*args, **kwargs) + return inner + return wrap + + def ensure_b64_encoding(val): """Safety method to ensure that values expected to be base64-encoded actually are. If they are, the value is returned unchanged. Otherwise, -- cgit From d5736e925f288462f6325130be0af49f0ace5884 Mon Sep 17 00:00:00 2001 From: Soren Hansen Date: Mon, 28 Feb 2011 23:31:09 +0100 Subject: Add a lock_path flag for lock files. --- nova/utils.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'nova/utils.py') diff --git a/nova/utils.py b/nova/utils.py index cb1ea5a7d..48f12350f 100644 --- a/nova/utils.py +++ b/nova/utils.py @@ -44,11 +44,13 @@ from eventlet.green import subprocess from nova import exception from nova.exception import ProcessExecutionError +from nova import flags from nova import log as logging LOG = logging.getLogger("nova.utils") TIME_FORMAT = "%Y-%m-%dT%H:%M:%SZ" +FLAGS = flags.FLAGS def import_class(import_str): @@ -495,7 +497,8 @@ def loads(s): def synchronized(name): def wrap(f): def inner(*args, **kwargs): - lock = lockfile.FileLock('nova-%s.lock' % name) + lock = lockfile.FileLock(os.path.join(FLAGS.lock_path, + 'nova-%s.lock' % name)) with lock: return f(*args, **kwargs) return inner -- cgit From be9004ffa4c70358c8edda1f33ffe7ba7e1ae1ee Mon Sep 17 00:00:00 2001 From: Soren Hansen Date: Tue, 1 Mar 2011 20:49:46 +0100 Subject: Use functools.wraps to make sure wrapped method's metadata (docstring and name) doesn't get mangled. --- nova/utils.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'nova/utils.py') diff --git a/nova/utils.py b/nova/utils.py index 48f12350f..9929e6fef 100644 --- a/nova/utils.py +++ b/nova/utils.py @@ -23,11 +23,14 @@ System-level utilities and helper functions. import base64 import datetime +import functools import inspect import json import lockfile +import netaddr import os import random +import re import socket import string import struct @@ -35,8 +38,6 @@ import sys import time import types from xml.sax import saxutils -import re -import netaddr from eventlet import event from eventlet import greenthread @@ -496,6 +497,7 @@ def loads(s): def synchronized(name): def wrap(f): + @functools.wraps(f) def inner(*args, **kwargs): lock = lockfile.FileLock(os.path.join(FLAGS.lock_path, 'nova-%s.lock' % name)) -- cgit From abd5779068f3b979fc79dec7a68549999c58092d Mon Sep 17 00:00:00 2001 From: Mark Washenberger Date: Fri, 4 Mar 2011 01:36:29 -0500 Subject: remove ensure_b64_encoding --- nova/utils.py | 12 ------------ 1 file changed, 12 deletions(-) (limited to 'nova/utils.py') diff --git a/nova/utils.py b/nova/utils.py index 0cf91e0cc..02b71900c 100644 --- a/nova/utils.py +++ b/nova/utils.py @@ -491,18 +491,6 @@ def loads(s): return json.loads(s) -def ensure_b64_encoding(val): - """Safety method to ensure that values expected to be base64-encoded - actually are. If they are, the value is returned unchanged. Otherwise, - the encoded value is returned. - """ - try: - dummy = base64.decode(val) - return val - except TypeError: - return base64.b64encode(val) - - 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, -- cgit From 7b7abe7e7a25c0cd07c64c34f69ce050c669cfc3 Mon Sep 17 00:00:00 2001 From: Soren Hansen Date: Mon, 7 Mar 2011 21:54:25 +0100 Subject: Log failed command execution if there are more retry attempts left. --- nova/utils.py | 1 + 1 file changed, 1 insertion(+) (limited to 'nova/utils.py') diff --git a/nova/utils.py b/nova/utils.py index 829adfb9e..80e5b6bbe 100644 --- a/nova/utils.py +++ b/nova/utils.py @@ -166,6 +166,7 @@ def execute(cmd, process_input=None, addl_env=None, check_exit_code=True, if not attempts: raise else: + LOG.debug(_("%r failed. Retrying."), cmd) greenthread.sleep(random.randint(20, 200) / 100.0) -- cgit From cac5881eaa35f94e004c18dd34ca78014f067976 Mon Sep 17 00:00:00 2001 From: Eric Windisch Date: Tue, 8 Mar 2011 01:01:41 -0500 Subject: execvp --- nova/utils.py | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) (limited to 'nova/utils.py') diff --git a/nova/utils.py b/nova/utils.py index 40a8d8d8c..c96b85294 100644 --- a/nova/utils.py +++ b/nova/utils.py @@ -125,15 +125,15 @@ def fetchfile(url, target): # c.perform() # c.close() # fp.close() - execute("curl","--fail",url,"-o",target) + execute("curl", "--fail", url, "-o", target) -def execute(cmd, process_input=None, addl_env=None, check_exit_code=True): - LOG.debug(_("Running cmd (subprocess): %s"), cmd) +def execute(*cmd, process_input=None, addl_env=None, check_exit_code=True): + LOG.debug(_("Running cmd (subprocess): %s"), ' '.join(cmd)) env = os.environ.copy() if addl_env: env.update(addl_env) - obj = subprocess.Popen(cmd, stdin=subprocess.PIPE, + obj = subprocess.Popen(*cmd, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, env=env) result = None if process_input != None: @@ -148,7 +148,7 @@ def execute(cmd, process_input=None, addl_env=None, check_exit_code=True): raise ProcessExecutionError(exit_code=obj.returncode, stdout=stdout, stderr=stderr, - cmd=cmd) + cmd=' '.join(cmd)) # NOTE(termie): this appears to be necessary to let the subprocess call # clean something up in between calls, without it two # execute calls in a row hangs the second one @@ -158,7 +158,7 @@ def execute(cmd, process_input=None, addl_env=None, check_exit_code=True): def ssh_execute(ssh, cmd, process_input=None, addl_env=None, check_exit_code=True): - LOG.debug(_("Running cmd (SSH): %s"), cmd) + LOG.debug(_("Running cmd (SSH): %s"), ' '.join(cmd)) if addl_env: raise exception.Error("Environment not supported over SSH") @@ -187,7 +187,7 @@ def ssh_execute(ssh, cmd, process_input=None, raise exception.ProcessExecutionError(exit_code=exit_status, stdout=stdout, stderr=stderr, - cmd=cmd) + cmd=' '.join(cmd)) return (stdout, stderr) @@ -254,7 +254,7 @@ def last_octet(address): def get_my_linklocal(interface): try: - if_str = execute("ip","-f","inet6","-o","addr","show", interface) + 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] -- cgit From a320b5df9f916adf8422ed312306c77570d392c2 Mon Sep 17 00:00:00 2001 From: Eric Windisch Date: Wed, 9 Mar 2011 00:30:05 -0500 Subject: execvp: almost passes tests --- nova/utils.py | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) (limited to 'nova/utils.py') diff --git a/nova/utils.py b/nova/utils.py index c96b85294..9b51f8b40 100644 --- a/nova/utils.py +++ b/nova/utils.py @@ -128,13 +128,20 @@ def fetchfile(url, target): execute("curl", "--fail", url, "-o", target) -def execute(*cmd, process_input=None, addl_env=None, check_exit_code=True): +def execute(*cmd, **kwargs): + process_input=kwargs.get('process_input', None) + addl_env=kwargs.get('addl_env', None) + check_exit_code=kwargs.get('check_exit_code', True) + stdin=kwargs.get('stdin', subprocess.PIPE) + stdout=kwargs.get('stdout', subprocess.PIPE) + stderr=kwargs.get('stderr', subprocess.PIPE) + LOG.debug(_("Running cmd (subprocess): %s"), ' '.join(cmd)) env = os.environ.copy() if addl_env: env.update(addl_env) - obj = subprocess.Popen(*cmd, stdin=subprocess.PIPE, - stdout=subprocess.PIPE, stderr=subprocess.PIPE, env=env) + obj = subprocess.Popen(cmd, stdin=stdin, + stdout=stdout, stderr=stderr, env=env) result = None if process_input != None: result = obj.communicate(process_input) @@ -220,9 +227,9 @@ def debug(arg): return arg -def runthis(prompt, cmd, check_exit_code=True): - LOG.debug(_("Running %s"), (cmd)) - rv, err = execute(cmd, check_exit_code=check_exit_code) +def runthis(prompt, *cmd, **kwargs): + LOG.debug(_("Running %s"), (" ".join(cmd))) + rv, err = execute(*cmd, **kwargs) def generate_uid(topic, size=8): -- cgit From 1d7358e70379607c9cce02307f4336efbd135a5d Mon Sep 17 00:00:00 2001 From: Eric Windisch Date: Wed, 9 Mar 2011 01:26:53 -0500 Subject: execvp: unit tests pass --- nova/utils.py | 1 + 1 file changed, 1 insertion(+) (limited to 'nova/utils.py') diff --git a/nova/utils.py b/nova/utils.py index 9b51f8b40..7ddf056ea 100644 --- a/nova/utils.py +++ b/nova/utils.py @@ -135,6 +135,7 @@ def execute(*cmd, **kwargs): stdin=kwargs.get('stdin', subprocess.PIPE) stdout=kwargs.get('stdout', subprocess.PIPE) stderr=kwargs.get('stderr', subprocess.PIPE) + cmd=map(str,cmd) LOG.debug(_("Running cmd (subprocess): %s"), ' '.join(cmd)) env = os.environ.copy() -- cgit From 23369a63f4b74fb64bf57554a3fd8b15e3e2b49c Mon Sep 17 00:00:00 2001 From: Eric Windisch Date: Wed, 9 Mar 2011 14:31:23 -0500 Subject: Fixes uses of process_input --- nova/utils.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'nova/utils.py') diff --git a/nova/utils.py b/nova/utils.py index 7ddf056ea..0937522ec 100644 --- a/nova/utils.py +++ b/nova/utils.py @@ -131,7 +131,7 @@ def fetchfile(url, target): def execute(*cmd, **kwargs): process_input=kwargs.get('process_input', None) addl_env=kwargs.get('addl_env', None) - check_exit_code=kwargs.get('check_exit_code', True) + check_exit_code=kwargs.get('check_exit_code', 0) stdin=kwargs.get('stdin', subprocess.PIPE) stdout=kwargs.get('stdout', subprocess.PIPE) stderr=kwargs.get('stderr', subprocess.PIPE) @@ -151,7 +151,7 @@ def execute(*cmd, **kwargs): obj.stdin.close() if obj.returncode: LOG.debug(_("Result was %s") % obj.returncode) - if check_exit_code and obj.returncode != 0: + if check_exit_code is not None and obj.returncode != check_exit_code: (stdout, stderr) = result raise ProcessExecutionError(exit_code=obj.returncode, stdout=stdout, -- cgit From fc9840bae6200c8f89fb8a3ba0ab45663c872b3c Mon Sep 17 00:00:00 2001 From: Eric Windisch Date: Wed, 9 Mar 2011 15:33:20 -0500 Subject: execvp passes pep8 --- nova/utils.py | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) (limited to 'nova/utils.py') diff --git a/nova/utils.py b/nova/utils.py index 0937522ec..3a4ec3c6a 100644 --- a/nova/utils.py +++ b/nova/utils.py @@ -40,7 +40,7 @@ import netaddr from eventlet import event from eventlet import greenthread from eventlet.green import subprocess - +None from nova import exception from nova.exception import ProcessExecutionError from nova import log as logging @@ -129,13 +129,13 @@ def fetchfile(url, target): def execute(*cmd, **kwargs): - process_input=kwargs.get('process_input', None) - addl_env=kwargs.get('addl_env', None) - check_exit_code=kwargs.get('check_exit_code', 0) - stdin=kwargs.get('stdin', subprocess.PIPE) - stdout=kwargs.get('stdout', subprocess.PIPE) - stderr=kwargs.get('stderr', subprocess.PIPE) - cmd=map(str,cmd) + process_input = kwargs.get('process_input', None) + addl_env = kwargs.get('addl_env', None) + check_exit_code = kwargs.get('check_exit_code', 0) + stdin = kwargs.get('stdin', subprocess.PIPE) + stdout = kwargs.get('stdout', subprocess.PIPE) + stderr = kwargs.get('stderr', subprocess.PIPE) + cmd = map(str, cmd) LOG.debug(_("Running cmd (subprocess): %s"), ' '.join(cmd)) env = os.environ.copy() @@ -151,7 +151,8 @@ def execute(*cmd, **kwargs): obj.stdin.close() if obj.returncode: LOG.debug(_("Result was %s") % obj.returncode) - if check_exit_code is not None and obj.returncode != check_exit_code: + if type(check_exit_code) == types.IntType \ + and obj.returncode != check_exit_code: (stdout, stderr) = result raise ProcessExecutionError(exit_code=obj.returncode, stdout=stdout, -- cgit From 11f2d788fd63c66af0e992f7b75b61273c059bcb Mon Sep 17 00:00:00 2001 From: Soren Hansen Date: Thu, 10 Mar 2011 21:31:47 +0100 Subject: PEP8 --- nova/utils.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'nova/utils.py') diff --git a/nova/utils.py b/nova/utils.py index 3008a512e..87e726394 100644 --- a/nova/utils.py +++ b/nova/utils.py @@ -166,9 +166,9 @@ def execute(*cmd, **kwargs): stdout=stdout, stderr=stderr, cmd=' '.join(cmd)) - # NOTE(termie): this appears to be necessary to let the subprocess call - # clean something up in between calls, without it two - # execute calls in a row hangs the second one + # NOTE(termie): this appears to be necessary to let the subprocess + # call clean something up in between calls, without + # it two execute calls in a row hangs the second one greenthread.sleep(0) return result except ProcessExecutionError: -- cgit From 9164b8d224ae6629cdac00248b98fad762bdfc10 Mon Sep 17 00:00:00 2001 From: Soren Hansen Date: Mon, 14 Mar 2011 10:46:26 +0100 Subject: Make utils.execute not overwrite std{in,out,err} args to Popen on retries. Make utils.execute reject unknown kwargs. Add a couple of unit tests for utils.execute. --- nova/utils.py | 25 +++++++++++++++---------- 1 file changed, 15 insertions(+), 10 deletions(-) (limited to 'nova/utils.py') diff --git a/nova/utils.py b/nova/utils.py index 87e726394..2a98411ea 100644 --- a/nova/utils.py +++ b/nova/utils.py @@ -133,13 +133,14 @@ def fetchfile(url, target): def execute(*cmd, **kwargs): - process_input = kwargs.get('process_input', None) - addl_env = kwargs.get('addl_env', None) - check_exit_code = kwargs.get('check_exit_code', 0) - stdin = kwargs.get('stdin', subprocess.PIPE) - stdout = kwargs.get('stdout', subprocess.PIPE) - stderr = kwargs.get('stderr', subprocess.PIPE) - attempts = kwargs.get('attempts', 1) + process_input = kwargs.pop('process_input', None) + addl_env = kwargs.pop('addl_env', None) + check_exit_code = kwargs.pop('check_exit_code', 0) + delay_on_retry = kwargs.pop('delay_on_retry', True) + attempts = kwargs.pop('attempts', 1) + if len(kwargs): + raise exception.Error(_('Got unknown keyword args ' + 'to utils.execute: %r') % kwargs) cmd = map(str, cmd) while attempts > 0: @@ -149,8 +150,11 @@ def execute(*cmd, **kwargs): env = os.environ.copy() if addl_env: env.update(addl_env) - obj = subprocess.Popen(cmd, stdin=stdin, - stdout=stdout, stderr=stderr, env=env) + obj = subprocess.Popen(cmd, + stdin=subprocess.PIPE, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + env=env) result = None if process_input != None: result = obj.communicate(process_input) @@ -176,7 +180,8 @@ def execute(*cmd, **kwargs): raise else: LOG.debug(_("%r failed. Retrying."), cmd) - greenthread.sleep(random.randint(20, 200) / 100.0) + if delay_on_retry: + greenthread.sleep(random.randint(20, 200) / 100.0) def ssh_execute(ssh, cmd, process_input=None, -- cgit From c94ec9a5bab6c07b402b68e2f4ff081247a27cda Mon Sep 17 00:00:00 2001 From: Justin Santa Barbara Date: Mon, 14 Mar 2011 14:17:58 -0700 Subject: Initial implementation of refresh instance states --- nova/utils.py | 9 +++++++++ 1 file changed, 9 insertions(+) (limited to 'nova/utils.py') diff --git a/nova/utils.py b/nova/utils.py index 87e726394..e93f489be 100644 --- a/nova/utils.py +++ b/nova/utils.py @@ -585,3 +585,12 @@ def get_from_path(items, path): return results else: return get_from_path(results, remainder) + + +def check_instance(obj, cls): + """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))) + #TODO(justinsb): Can we make this better?? + return cls() # Ugly PyLint hack -- cgit From da605eb84f7d5de741225ff936447db01690a04f Mon Sep 17 00:00:00 2001 From: Justin Santa Barbara Date: Mon, 14 Mar 2011 20:48:33 -0700 Subject: Don't generate insecure passwords where it's easy to use urandom instead --- nova/utils.py | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) (limited to 'nova/utils.py') diff --git a/nova/utils.py b/nova/utils.py index 87e726394..9c8b27d56 100644 --- a/nova/utils.py +++ b/nova/utils.py @@ -263,12 +263,17 @@ def generate_mac(): def generate_password(length=20): - """Generate a random sequence of letters and digits - to be used as a password. Note that this is not intended - to represent the ultimate in security. + """Generate a random alphanumeric password, avoiding 'confusing' O,0,I,1. + + Believed to be reasonably secure (with a reasonable password length!) """ - chrs = string.letters + string.digits - return "".join([random.choice(chrs) for i in xrange(length)]) + # 26 letters, 10 digits = 36 + # Remove O, 0, I, 1 => 32 digits + # 32 digits means we're just using the low 5 bit of each byte + chrs = "ABCDEFGHJKLMNPQRSTUVWXYZ23456789" + + random_bytes = os.urandom(length) + return "".join([chrs[ord(random_bytes[i]) % 32] for i in xrange(length)]) def last_octet(address): -- cgit From 3d0cde272e3227978c5875c811c93e1e3df692ed Mon Sep 17 00:00:00 2001 From: Justin Santa Barbara Date: Mon, 14 Mar 2011 21:01:48 -0700 Subject: Clarify the logic in using 32 symbols --- nova/utils.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) (limited to 'nova/utils.py') diff --git a/nova/utils.py b/nova/utils.py index 9c8b27d56..0510c3cbe 100644 --- a/nova/utils.py +++ b/nova/utils.py @@ -267,9 +267,10 @@ def generate_password(length=20): Believed to be reasonably secure (with a reasonable password length!) """ - # 26 letters, 10 digits = 36 - # Remove O, 0, I, 1 => 32 digits - # 32 digits means we're just using the low 5 bit of each byte + # 26 letters, 10 digits = 36 choices + # Remove O, 0, I, 1 => 32 choices + # 32 choices means we're just using the low 5 bit of each byte, + # so there's no bias introduced by using a modulo chrs = "ABCDEFGHJKLMNPQRSTUVWXYZ23456789" random_bytes = os.urandom(length) -- cgit From 22aad6700124411aceed0b2bd3953cbbc48b6130 Mon Sep 17 00:00:00 2001 From: Justin Santa Barbara Date: Tue, 15 Mar 2011 11:24:07 -0700 Subject: Use random.SystemRandom for easy secure randoms, configurable symbol set by default including mixed-case --- nova/utils.py | 26 ++++++++++++++++---------- 1 file changed, 16 insertions(+), 10 deletions(-) (limited to 'nova/utils.py') diff --git a/nova/utils.py b/nova/utils.py index 0510c3cbe..199ee8701 100644 --- a/nova/utils.py +++ b/nova/utils.py @@ -262,19 +262,25 @@ def generate_mac(): return ':'.join(map(lambda x: "%02x" % x, mac)) -def generate_password(length=20): - """Generate a random alphanumeric password, avoiding 'confusing' O,0,I,1. +# 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 + + +# ~5 bits per symbol +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!) """ - # 26 letters, 10 digits = 36 choices - # Remove O, 0, I, 1 => 32 choices - # 32 choices means we're just using the low 5 bit of each byte, - # so there's no bias introduced by using a modulo - chrs = "ABCDEFGHJKLMNPQRSTUVWXYZ23456789" - - random_bytes = os.urandom(length) - return "".join([chrs[ord(random_bytes[i]) % 32] for i in xrange(length)]) + r = random.SystemRandom() + return "".join([r.choice(symbols) for _i in xrange(length)]) def last_octet(address): -- cgit From 0eaf02efd5fef3f77fced9c1a71c32a6f14f293f Mon Sep 17 00:00:00 2001 From: Josh Kearney Date: Tue, 15 Mar 2011 16:21:22 -0500 Subject: Add logging to lock check --- nova/utils.py | 3 +++ 1 file changed, 3 insertions(+) (limited to 'nova/utils.py') diff --git a/nova/utils.py b/nova/utils.py index 87e726394..d6f9ba829 100644 --- a/nova/utils.py +++ b/nova/utils.py @@ -518,6 +518,9 @@ def synchronized(name): def wrap(f): @functools.wraps(f) def inner(*args, **kwargs): + LOG.debug(_("Attempting to grab %(lock)s for method " + "%(method)s..." % {"lock": name, + "method": f.__name__})) lock = lockfile.FileLock(os.path.join(FLAGS.lock_path, 'nova-%s.lock' % name)) with lock: -- cgit From d1860ce5d26fbbadb2310e8225e924879cde9a6c Mon Sep 17 00:00:00 2001 From: Soren Hansen Date: Tue, 22 Mar 2011 10:35:43 +0100 Subject: Make synchronized support both external (file based) locks as well as internal (semaphore based) locks. Attempt to make it native thread safe at the expense of never cleaning up semaphores. --- nova/utils.py | 67 ++++++++++++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 60 insertions(+), 7 deletions(-) (limited to 'nova/utils.py') diff --git a/nova/utils.py b/nova/utils.py index 499af2039..8936614cc 100644 --- a/nova/utils.py +++ b/nova/utils.py @@ -41,6 +41,7 @@ from xml.sax import saxutils from eventlet import event from eventlet import greenthread +from eventlet import semaphore from eventlet.green import subprocess None from nova import exception @@ -531,17 +532,69 @@ def loads(s): return json.loads(s) -def synchronized(name): +_semaphores_semaphore = semaphore.Semaphore() +_semaphores = {} + + +class _NoopContextManager(object): + def __enter__(self): + pass + + def __exit__(self, exc_type, exc_val, exc_tb): + pass + + +def synchronized(name, external=False): + """Synchronization decorator + + Decorating a method like so: + @synchronized('mylock') + def foo(self, *args): + ... + + ensures that only one thread will execute the bar method at a time. + + Different methods can share the same lock: + @synchronized('mylock') + def foo(self, *args): + ... + + @synchronized('mylock') + def bar(self, *args): + ... + + This way only one of either foo or bar can be executing at a time. + + The external keyword argument denotes whether this lock should work across + 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): @functools.wraps(f) def inner(*args, **kwargs): - LOG.debug(_("Attempting to grab %(lock)s for method " - "%(method)s..." % {"lock": name, + with _semaphores_semaphore: + if name not in _semaphores: + _semaphores[name] = semaphore.Semaphore() + sem = _semaphores[name] + LOG.debug(_('Attempting to grab semaphore "%(lock)s" for method ' + '"%(method)s"...' % {"lock": name, "method": f.__name__})) - lock = lockfile.FileLock(os.path.join(FLAGS.lock_path, - 'nova-%s.lock' % name)) - with lock: - return f(*args, **kwargs) + with sem: + if external: + LOG.debug(_('Attempting to grab file lock "%(lock)s" for ' + 'method "%(method)s"...' % + {"lock": name, "method": f.__name__})) + lock_file_path = os.path.join(FLAGS.lock_path, + 'nova-%s.lock' % name) + lock = lockfile.FileLock(lock_file_path) + else: + lock = _NoopContextManager() + + with lock: + return f(*args, **kwargs) + return inner return wrap -- cgit From 60a3aa86db1d0e1ea2f680c9587881e45fa99336 Mon Sep 17 00:00:00 2001 From: Soren Hansen Date: Tue, 22 Mar 2011 14:14:47 +0100 Subject: Make synchronized decorator not leak semaphores, at the expense of not being truly thread safe (but safe enough for Eventlet style green threads). --- nova/utils.py | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) (limited to 'nova/utils.py') diff --git a/nova/utils.py b/nova/utils.py index 8936614cc..c580e805a 100644 --- a/nova/utils.py +++ b/nova/utils.py @@ -574,10 +574,12 @@ def synchronized(name, external=False): def wrap(f): @functools.wraps(f) def inner(*args, **kwargs): - with _semaphores_semaphore: - if name not in _semaphores: - _semaphores[name] = semaphore.Semaphore() - sem = _semaphores[name] + # NOTE(soren): If we ever go natively threaded, this will be racy. + # See http://stackoverflow.com/questions/5390569/dyn\ + # amically-allocating-and-destroying-mutexes + if name not in _semaphores: + _semaphores[name] = semaphore.Semaphore() + sem = _semaphores[name] LOG.debug(_('Attempting to grab semaphore "%(lock)s" for method ' '"%(method)s"...' % {"lock": name, "method": f.__name__})) @@ -593,8 +595,14 @@ def synchronized(name, external=False): lock = _NoopContextManager() with lock: - return f(*args, **kwargs) + retval = f(*args, **kwargs) + # If no-one else is waiting for it, delete it. + # See note about possible raciness above. + if not sem.balance < 1: + del _semaphores[name] + + return retval return inner return wrap -- cgit From 06815cb729d8687403fc736ae6125c26867f42b3 Mon Sep 17 00:00:00 2001 From: Soren Hansen Date: Tue, 22 Mar 2011 17:13:48 +0100 Subject: Remove unused global semaphore. --- nova/utils.py | 1 - 1 file changed, 1 deletion(-) (limited to 'nova/utils.py') diff --git a/nova/utils.py b/nova/utils.py index c580e805a..8b9ce4734 100644 --- a/nova/utils.py +++ b/nova/utils.py @@ -532,7 +532,6 @@ def loads(s): return json.loads(s) -_semaphores_semaphore = semaphore.Semaphore() _semaphores = {} -- cgit From 789fcb46915dce5fa533357ac462040ec6aa8968 Mon Sep 17 00:00:00 2001 From: Rick Harris Date: Tue, 22 Mar 2011 20:26:45 +0000 Subject: Adding BASE_IMAGE_ATTRS to ImageService --- nova/utils.py | 36 ++++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) (limited to 'nova/utils.py') diff --git a/nova/utils.py b/nova/utils.py index 199ee8701..96a51b425 100644 --- a/nova/utils.py +++ b/nova/utils.py @@ -597,3 +597,39 @@ def get_from_path(items, path): return results else: return get_from_path(results, remainder) + + +def flatten_dict(dict_, flattened=None): + """Recursively flatten a nested dictionary""" + flattened = flattened or {} + for key, value in dict_.iteritems(): + if hasattr(value, 'iteritems'): + flatten_dict(value, flattened) + else: + flattened[key] = value + return flattened + + +def partition_dict(dict_, keys): + """Return two dicts, one containing only `keys` the other containing + everything but `keys` + """ + intersection = {} + difference = {} + for key, value in dict_.iteritems(): + if key in keys: + intersection[key] = value + else: + difference[key] = value + return intersection, difference + + +def map_dict_keys(dict_, key_map): + """Return a dictionary 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 + mapped[mapped_key] = value + return mapped -- cgit From d8176eda3f31973a8718b98f35e202ff61c48bbc Mon Sep 17 00:00:00 2001 From: Rick Harris Date: Tue, 22 Mar 2011 20:34:00 +0000 Subject: Pep8 fix --- nova/utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'nova/utils.py') diff --git a/nova/utils.py b/nova/utils.py index f43ba2e7e..7b96a0daf 100644 --- a/nova/utils.py +++ b/nova/utils.py @@ -621,7 +621,7 @@ def partition_dict(dict_, keys): def map_dict_keys(dict_, key_map): - """Return a dictionary in which the dictionaries keys are mapped to + """Return a dictionary in which the dictionaries keys are mapped to new keys. """ mapped = {} -- cgit From 365b98f4d52740ef85f8a8f098a32e441d7ac168 Mon Sep 17 00:00:00 2001 From: Justin Santa Barbara Date: Tue, 22 Mar 2011 21:42:17 -0700 Subject: Renamed check_instance -> check_isinstance to make intent clearer --- nova/utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'nova/utils.py') diff --git a/nova/utils.py b/nova/utils.py index e93f489be..2e653bda0 100644 --- a/nova/utils.py +++ b/nova/utils.py @@ -587,7 +587,7 @@ def get_from_path(items, path): return get_from_path(results, remainder) -def check_instance(obj, cls): +def check_isinstance(obj, cls): """Checks that obj is of type cls, and lets PyLint infer types""" if isinstance(obj, cls): return obj -- cgit From a4d78e44d7ca35a6cca4454667cab743409fd95a Mon Sep 17 00:00:00 2001 From: Justin Santa Barbara Date: Tue, 22 Mar 2011 22:45:15 -0700 Subject: Added space in between # and TODO in #TODO --- nova/utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'nova/utils.py') diff --git a/nova/utils.py b/nova/utils.py index 2e653bda0..36b384f4f 100644 --- a/nova/utils.py +++ b/nova/utils.py @@ -592,5 +592,5 @@ def check_isinstance(obj, cls): if isinstance(obj, cls): return obj raise Exception(_("Expected object of type: %s") % (str(cls))) - #TODO(justinsb): Can we make this better?? + # TODO(justinsb): Can we make this better?? return cls() # Ugly PyLint hack -- cgit From a7c9ad393f72b49515a445504a5bc87f8a26932c Mon Sep 17 00:00:00 2001 From: Rick Harris Date: Wed, 23 Mar 2011 05:50:53 +0000 Subject: Filtering images by user_id now --- nova/utils.py | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'nova/utils.py') diff --git a/nova/utils.py b/nova/utils.py index 7b96a0daf..d114cb14f 100644 --- a/nova/utils.py +++ b/nova/utils.py @@ -629,3 +629,9 @@ def map_dict_keys(dict_, key_map): mapped_key = key_map[key] if key in key_map else key mapped[mapped_key] = value return mapped + + +def subset_dict(dict_, keys): + """Return a dict that only contains a subset of keys""" + subset = partition_dict(dict_, keys)[0] + return subset -- cgit From abb764f51385a0b811b23379d78f7db027d4cca5 Mon Sep 17 00:00:00 2001 From: Josh Kearney Date: Wed, 23 Mar 2011 14:41:35 -0500 Subject: Automatically unrescue instances after a given timeout --- nova/utils.py | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'nova/utils.py') diff --git a/nova/utils.py b/nova/utils.py index 499af2039..38cdb8021 100644 --- a/nova/utils.py +++ b/nova/utils.py @@ -334,6 +334,13 @@ def utcnow(): utcnow.override_time = None +def is_then_greater(then, seconds): + if utcnow() - then > datetime.timedelta(seconds=seconds): + return True + else: + return False + + def utcnow_ts(): """Timestamp version of our utcnow function.""" return time.mktime(utcnow().timetuple()) -- cgit From 3c0fcc47be08ac4f3d508fd46f3b95036899aaad Mon Sep 17 00:00:00 2001 From: termie Date: Wed, 23 Mar 2011 13:39:01 -0700 Subject: fix utils.execute retries for osx also some minor misc cleanups --- nova/utils.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) (limited to 'nova/utils.py') diff --git a/nova/utils.py b/nova/utils.py index 499af2039..249470636 100644 --- a/nova/utils.py +++ b/nova/utils.py @@ -170,10 +170,6 @@ def execute(*cmd, **kwargs): stdout=stdout, stderr=stderr, cmd=' '.join(cmd)) - # NOTE(termie): this appears to be necessary to let the subprocess - # call clean something up in between calls, without - # it two execute calls in a row hangs the second one - greenthread.sleep(0) return result except ProcessExecutionError: if not attempts: @@ -182,6 +178,11 @@ def execute(*cmd, **kwargs): LOG.debug(_("%r failed. Retrying."), cmd) if delay_on_retry: greenthread.sleep(random.randint(20, 200) / 100.0) + finally: + # NOTE(termie): this appears to be necessary to let the subprocess + # call clean something up in between calls, without + # it two execute calls in a row hangs the second one + greenthread.sleep(0) def ssh_execute(ssh, cmd, process_input=None, -- cgit From 95fa499f1a7718694e37a747a6a5a0e309ce877d Mon Sep 17 00:00:00 2001 From: Eldar Nugaev Date: Thu, 24 Mar 2011 00:36:07 +0300 Subject: migration gateway_v6 to network_info --- nova/utils.py | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) (limited to 'nova/utils.py') diff --git a/nova/utils.py b/nova/utils.py index 499af2039..44234813f 100644 --- a/nova/utils.py +++ b/nova/utils.py @@ -309,11 +309,15 @@ def get_my_linklocal(interface): def to_global_ipv6(prefix, mac): - 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() + 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): -- cgit From b3a8c70304672abe9b461c6cfeed3e8b517ca0b6 Mon Sep 17 00:00:00 2001 From: Josh Kearney Date: Wed, 23 Mar 2011 16:56:54 -0500 Subject: Added docstring --- nova/utils.py | 1 + 1 file changed, 1 insertion(+) (limited to 'nova/utils.py') diff --git a/nova/utils.py b/nova/utils.py index 38cdb8021..bf1aa4a91 100644 --- a/nova/utils.py +++ b/nova/utils.py @@ -335,6 +335,7 @@ utcnow.override_time = None def is_then_greater(then, seconds): + """Return True of 'then' is greater than 'seconds'""" if utcnow() - then > datetime.timedelta(seconds=seconds): return True else: -- cgit From 5170e8b5dd96cf8c7bb91e84203cfaebb099af46 Mon Sep 17 00:00:00 2001 From: Eldar Nugaev Date: Thu, 24 Mar 2011 00:56:56 +0300 Subject: small fix --- nova/utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'nova/utils.py') diff --git a/nova/utils.py b/nova/utils.py index 44234813f..fabc01532 100644 --- a/nova/utils.py +++ b/nova/utils.py @@ -317,7 +317,7 @@ def to_global_ipv6(prefix, mac): return (mac64_addr ^ netaddr.IPAddress('::0200:0:0:0') | maskIP).\ format() except TypeError: - raise TypeError(_("Bad mac for to_global_ipv6: %s" % mac)) + raise TypeError(_("Bad mac for to_global_ipv6: %s") % mac) def to_mac(ipv6_address): -- cgit From a12b6f0a0808fba5541723a537118447b55b69ad Mon Sep 17 00:00:00 2001 From: Josh Kearney Date: Wed, 23 Mar 2011 17:15:41 -0500 Subject: Better method name --- nova/utils.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'nova/utils.py') diff --git a/nova/utils.py b/nova/utils.py index bf1aa4a91..04b6c9778 100644 --- a/nova/utils.py +++ b/nova/utils.py @@ -334,9 +334,9 @@ def utcnow(): utcnow.override_time = None -def is_then_greater(then, seconds): - """Return True of 'then' is greater than 'seconds'""" - if utcnow() - then > datetime.timedelta(seconds=seconds): +def is_older_than(before, seconds): + """Return True if before is older than 'seconds'""" + if utcnow() - before > datetime.timedelta(seconds=seconds): return True else: return False -- cgit From cf70a1a76dfa0456ea6230eaa014fa98e7ddd464 Mon Sep 17 00:00:00 2001 From: Josh Kearney Date: Thu, 24 Mar 2011 10:30:09 -0500 Subject: Small refactor --- nova/utils.py | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) (limited to 'nova/utils.py') diff --git a/nova/utils.py b/nova/utils.py index 03a6e8095..6042a5332 100644 --- a/nova/utils.py +++ b/nova/utils.py @@ -336,11 +336,8 @@ utcnow.override_time = None def is_older_than(before, seconds): - """Return True if before is older than 'seconds'""" - if utcnow() - before > datetime.timedelta(seconds=seconds): - return True - else: - return False + """Return True if before is older than seconds""" + return utcnow() - before > datetime.timedelta(seconds=seconds) def utcnow_ts(): -- cgit From c3b98443263de944aa54ae4948330b6cfb9a02a6 Mon Sep 17 00:00:00 2001 From: Ilya Alekseyev Date: Thu, 24 Mar 2011 23:00:16 +0300 Subject: style and spacing fixed --- nova/utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'nova/utils.py') diff --git a/nova/utils.py b/nova/utils.py index d3375abc3..29e33ad9e 100644 --- a/nova/utils.py +++ b/nova/utils.py @@ -316,7 +316,7 @@ def to_global_ipv6(prefix, mac): mac64_addr = netaddr.IPAddress(int_addr) maskIP = netaddr.IPNetwork(prefix).ip return (mac64_addr ^ netaddr.IPAddress('::0200:0:0:0') | maskIP).\ - format() + format() except TypeError: raise TypeError(_("Bad mac for to_global_ipv6: %s") % mac) -- cgit