summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--cinder/openstack/common/processutils.py21
-rw-r--r--cinder/openstack/common/strutils.py74
-rw-r--r--cinder/tests/brick/fake_lvm.py3
-rw-r--r--cinder/volume/drivers/lvm.py2
4 files changed, 93 insertions, 7 deletions
diff --git a/cinder/openstack/common/processutils.py b/cinder/openstack/common/processutils.py
index 059f570b1..509833e3d 100644
--- a/cinder/openstack/common/processutils.py
+++ b/cinder/openstack/common/processutils.py
@@ -19,6 +19,7 @@
System-level utilities and helper functions.
"""
+import logging
import os
import random
import shlex
@@ -28,7 +29,7 @@ from eventlet.green import subprocess
from eventlet import greenthread
from cinder.openstack.common.gettextutils import _
-from cinder.openstack.common import log as logging
+from cinder.openstack.common import strutils
LOG = logging.getLogger(__name__)
@@ -102,6 +103,8 @@ def execute(*cmd, **kwargs):
:param shell: whether or not there should be a shell used to
execute this command. Defaults to false.
:type shell: boolean
+ :param loglevel: log level for execute commands.
+ :type loglevel: int. (Should be logging.DEBUG or logging.INFO)
:returns: (stdout, stderr) from process execution
:raises: :class:`UnknownArgumentError` on
receiving unknown arguments
@@ -116,6 +119,7 @@ def execute(*cmd, **kwargs):
run_as_root = kwargs.pop('run_as_root', False)
root_helper = kwargs.pop('root_helper', '')
shell = kwargs.pop('shell', False)
+ loglevel = kwargs.pop('loglevel', logging.DEBUG)
if isinstance(check_exit_code, bool):
ignore_exit_code = not check_exit_code
@@ -135,11 +139,12 @@ def execute(*cmd, **kwargs):
cmd = shlex.split(root_helper) + list(cmd)
cmd = map(str, cmd)
+ sanitized_cmd = strutils.mask_password(' '.join(cmd))
while attempts > 0:
attempts -= 1
try:
- LOG.debug(_('Running cmd (subprocess): %s'), ' '.join(cmd))
+ LOG.log(loglevel, _('Running cmd (subprocess): %s'), sanitized_cmd)
_PIPE = subprocess.PIPE # pylint: disable=E1101
if os.name == 'nt':
@@ -164,19 +169,21 @@ def execute(*cmd, **kwargs):
obj.stdin.close() # pylint: disable=E1101
_returncode = obj.returncode # pylint: disable=E1101
if _returncode:
- LOG.debug(_('Result was %s') % _returncode)
+ LOG.log(loglevel, _('Result was %s') % _returncode)
if not ignore_exit_code and _returncode not in check_exit_code:
(stdout, stderr) = result
+ sanitized_stdout = strutils.mask_password(stdout)
+ sanitized_stderr = strutils.mask_password(stderr)
raise ProcessExecutionError(exit_code=_returncode,
- stdout=stdout,
- stderr=stderr,
- cmd=' '.join(cmd))
+ stdout=sanitized_stdout,
+ stderr=sanitized_stderr,
+ cmd=sanitized_cmd)
return result
except ProcessExecutionError:
if not attempts:
raise
else:
- LOG.debug(_('%r failed. Retrying.'), cmd)
+ LOG.log(loglevel, _('%r failed. Retrying.'), sanitized_cmd)
if delay_on_retry:
greenthread.sleep(random.randint(20, 200) / 100.0)
finally:
diff --git a/cinder/openstack/common/strutils.py b/cinder/openstack/common/strutils.py
index 8cd996599..488f3e04f 100644
--- a/cinder/openstack/common/strutils.py
+++ b/cinder/openstack/common/strutils.py
@@ -23,6 +23,8 @@ import re
import sys
import unicodedata
+import six
+
from cinder.openstack.common.gettextutils import _
@@ -44,6 +46,39 @@ SLUGIFY_STRIP_RE = re.compile(r"[^\w\s-]")
SLUGIFY_HYPHENATE_RE = re.compile(r"[-\s]+")
+# NOTE(flaper87): The following globals are used by `mask_password`
+_SANITIZE_KEYS = ['adminPass', 'admin_pass', 'password', 'admin_password']
+
+# NOTE(ldbragst): Let's build a list of regex objects using the list of
+# _SANITIZE_KEYS we already have. This way, we only have to add the new key
+# to the list of _SANITIZE_KEYS and we can generate regular expressions
+# for XML and JSON automatically.
+_SANITIZE_PATTERNS_2 = []
+_SANITIZE_PATTERNS_1 = []
+
+# NOTE(amrith): Some regular expressions have only one parameter, some
+# have two parameters. Use different lists of patterns here.
+_FORMAT_PATTERNS_1 = [r'(%(key)s\s*[=]\s*)[^\s^\'^\"]+']
+_FORMAT_PATTERNS_2 = [r'(%(key)s\s*[=]\s*[\"\']).*?([\"\'])',
+ r'(%(key)s\s+[\"\']).*?([\"\'])',
+ r'([-]{2}%(key)s\s+)[^\'^\"^=^\s]+([\s]*)',
+ r'(<%(key)s>).*?(</%(key)s>)',
+ r'([\"\']%(key)s[\"\']\s*:\s*[\"\']).*?([\"\'])',
+ r'([\'"].*?%(key)s[\'"]\s*:\s*u?[\'"]).*?([\'"])',
+ r'([\'"].*?%(key)s[\'"]\s*,\s*\'--?[A-z]+\'\s*,\s*u?'
+ '[\'"]).*?([\'"])',
+ r'(%(key)s\s*--?[A-z]+\s*)\S+(\s*)']
+
+for key in _SANITIZE_KEYS:
+ for pattern in _FORMAT_PATTERNS_2:
+ reg_ex = re.compile(pattern % {'key': key}, re.DOTALL)
+ _SANITIZE_PATTERNS_2.append(reg_ex)
+
+ for pattern in _FORMAT_PATTERNS_1:
+ reg_ex = re.compile(pattern % {'key': key}, re.DOTALL)
+ _SANITIZE_PATTERNS_1.append(reg_ex)
+
+
def int_from_bool_as_string(subject):
"""Interpret a string as a boolean and return either 1 or 0.
@@ -214,3 +249,42 @@ def to_slug(value, incoming=None, errors="strict"):
"ascii", "ignore").decode("ascii")
value = SLUGIFY_STRIP_RE.sub("", value).strip().lower()
return SLUGIFY_HYPHENATE_RE.sub("-", value)
+
+
+def mask_password(message, secret="***"):
+ """Replace password with 'secret' in message.
+
+ :param message: The string which includes security information.
+ :param secret: value with which to replace passwords.
+ :returns: The unicode value of message with the password fields masked.
+
+ For example:
+
+ >>> mask_password("'adminPass' : 'aaaaa'")
+ "'adminPass' : '***'"
+ >>> mask_password("'admin_pass' : 'aaaaa'")
+ "'admin_pass' : '***'"
+ >>> mask_password('"password" : "aaaaa"')
+ '"password" : "***"'
+ >>> mask_password("'original_password' : 'aaaaa'")
+ "'original_password' : '***'"
+ >>> mask_password("u'original_password' : u'aaaaa'")
+ "u'original_password' : u'***'"
+ """
+ message = six.text_type(message)
+
+ # NOTE(ldbragst): Check to see if anything in message contains any key
+ # specified in _SANITIZE_KEYS, if not then just return the message since
+ # we don't have to mask any passwords.
+ if not any(key in message for key in _SANITIZE_KEYS):
+ return message
+
+ substitute = r'\g<1>' + secret + r'\g<2>'
+ for pattern in _SANITIZE_PATTERNS_2:
+ message = re.sub(pattern, substitute, message)
+
+ substitute = r'\g<1>' + secret
+ for pattern in _SANITIZE_PATTERNS_1:
+ message = re.sub(pattern, substitute, message)
+
+ return message
diff --git a/cinder/tests/brick/fake_lvm.py b/cinder/tests/brick/fake_lvm.py
index a6f76ab9b..d6eec28f5 100644
--- a/cinder/tests/brick/fake_lvm.py
+++ b/cinder/tests/brick/fake_lvm.py
@@ -67,3 +67,6 @@ class FakeBrickLVM(object):
def lv_has_snapshot(self, name):
return False
+
+ def activate_lv(self, lv, is_snapshot=False):
+ pass
diff --git a/cinder/volume/drivers/lvm.py b/cinder/volume/drivers/lvm.py
index d6ccdca33..f337c7192 100644
--- a/cinder/volume/drivers/lvm.py
+++ b/cinder/volume/drivers/lvm.py
@@ -314,6 +314,8 @@ class LVMVolumeDriver(driver.VolumeDriver):
self.configuration.lvm_type,
mirror_count)
+ self.vg.activate_lv(temp_snapshot['name'], is_snapshot=True)
+
try:
volutils.copy_volume(self.local_path(temp_snapshot),
self.local_path(volume),