summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorHansen, Tony (th1395) <th1395@att.com>2018-08-22 00:52:37 +0000
committerHansen, Tony (th1395) <th1395@att.com>2018-08-22 00:53:55 +0000
commit03bac46783200b036b769411b773c87d0ee2cd1e (patch)
tree7bc1bb16c5667a31aa29d2c37cae0fac87a091be
parent59e33384d34340c2608e089bb03c5c118ff15142 (diff)
add mechanism to force a password change
for plugins/pgaas_plugin Change-Id: Iac969f330683304bc6b6f6a81187cf75ac70815e Signed-off-by: Hansen, Tony (th1395) <th1395@att.com> Issue-ID: CCSDK-479 Signed-off-by: Hansen, Tony (th1395) <th1395@att.com>
-rw-r--r--pgaas/pgaas/pgaas_plugin.py91
-rw-r--r--pgaas/pgaas_types.yaml1
-rw-r--r--pgaas/tests/test_plugin.py27
3 files changed, 102 insertions, 17 deletions
diff --git a/pgaas/pgaas/pgaas_plugin.py b/pgaas/pgaas/pgaas_plugin.py
index d04cc2e..a5e4440 100644
--- a/pgaas/pgaas/pgaas_plugin.py
+++ b/pgaas/pgaas/pgaas_plugin.py
@@ -16,6 +16,8 @@
# limitations under the License.
# ============LICENSE_END======================================================
+from __future__ import print_function
+
from cloudify import ctx
from cloudify.decorators import operation
from cloudify.exceptions import NonRecoverableError
@@ -228,7 +230,7 @@ def rootdesc(data, dbname, initialpassword=None):
'host': hostportion(data['rw']),
'port': portportion(data['rw']),
'user': 'postgres',
- 'password': initialpassword if initialpassword else getpass(data, 'postgres')
+ 'password': initialpassword if initialpassword else getpass(data, 'postgres', data['rw'], 'postgres')
}
def rootconn(data, dbname='postgres', initialpassword=None):
@@ -250,7 +252,7 @@ def onedesc(data, dbname, role, access):
'host': hostportion(data[access]),
'port': portportion(data[access]),
'user': user,
- 'password': getpass(data, user)
+ 'password': getpass(data, user, data['rw'], dbname)
}
def dbdescs(data, dbname):
@@ -263,12 +265,26 @@ def dbdescs(data, dbname):
'viewer': onedesc(data, dbname, 'viewer', 'ro')
}
-def getpass(data, ident):
+def getpass(data, ident, hostport, dbname):
"""
generate the password for a given user on a specific server
"""
m = hashlib.sha256()
m.update(ident)
+
+ # mix in the seed (the last line) for that database, if one exists
+ hostport = hostport.lower()
+ dbname = dbname.lower()
+ hostPortDbname = '{0}/pgaas/{1}:{2}'.format(OPT_MANAGER_RESOURCES, hostport, dbname)
+ try:
+ lastLine = ''
+ with open(hostPortDbname, "r") as fp:
+ for line in fp:
+ lastLine = line.strip()
+ m.update(line)
+ except IOError:
+ pass
+
m.update(base64.b64decode(data['data']))
return m.hexdigest()
@@ -290,7 +306,7 @@ def chkfqdn(fqdn):
verify that a FQDN is valid
"""
hp = hostportion(fqdn)
- pp = portportion(fqdn)
+ # not needed right now: pp = portportion(fqdn)
# TODO need to augment this for IPv6 addresses
return re.match('^[a-zA-Z0-9_-]+(\\.[a-zA-Z0-9_-]+)+$', hp) is not None
@@ -317,15 +333,15 @@ def getclusterinfo(wfqdn, reuse, rfqdn, initialpassword, related):
if len(related) != 0:
raiseNonRecoverableError('Cluster SSH keypair must not be specified when using an existing cluster')
try:
- fn = '{0}/pgaas/{1}'.format(OPT_MANAGER_RESOURCES, wfqdn).lower()
- with open('{0}/pgaas/{1}'.format(OPT_MANAGER_RESOURCES, wfqdn).lower(), 'r') as f:
+ fn = '{0}/pgaas/{1}'.format(OPT_MANAGER_RESOURCES, wfqdn.lower())
+ with open(fn, 'r') as f:
data = json.load(f)
data['rw'] = wfqdn
return data
except Exception as e:
warn("Error: {0}".format(e))
warn("Stack: {0}".format(traceback.format_exc()))
- raiseNonRecoverableError('Cluster must be deployed when using an existing cluster: fqdn={0}, err={1}'.format(safestr(wfqdn),e))
+ raiseNonRecoverableError('Cluster must be deployed when using an existing cluster. Check your domain name: fqdn={0}, err={1}'.format(safestr(wfqdn),e))
if rfqdn == '':
rfqdn = wfqdn
elif not chkfqdn(rfqdn):
@@ -339,17 +355,17 @@ def getclusterinfo(wfqdn, reuse, rfqdn, initialpassword, related):
except:
pass
try:
- with open('{0}/pgaas/{1}'.format(OPT_MANAGER_RESOURCES, wfqdn).lower(), 'w') as f:
+ with open('{0}/pgaas/{1}'.format(OPT_MANAGER_RESOURCES, wfqdn.lower()), 'w') as f:
f.write(json.dumps(data))
except Exception as e:
warn("Error: {0}".format(e))
warn("Stack: {0}".format(traceback.format_exc()))
- raiseNonRecoverableError('Cannot write cluster information to {0}/pgaas: fqdn={1}, err={2}'.format(OPT_MANAGER_RESOURCES, safestr(wfqdn),e))
+ raiseNonRecoverableError('Cannot write cluster information to {0}/pgaas: fqdn={1}, err={2}'.format(OPT_MANAGER_RESOURCES, safestr(wfqdn), e))
data['rw'] = wfqdn
if initialpassword:
with rootconn(data, initialpassword=initialpassword) as conn:
crr = conn.cursor()
- dbexecute(crr, "ALTER USER postgres WITH PASSWORD %s", (getpass(data, 'postgres'),))
+ dbexecute(crr, "ALTER USER postgres WITH PASSWORD %s", (getpass(data, 'postgres', wfqdn, 'postgres'),))
crr.close()
return(data)
@@ -368,7 +384,7 @@ def add_pgaas_cluster(**kwargs):
find_related_nodes('dcae.relationships.pgaas_cluster_uses_sshkeypair'))
ctx.instance.runtime_properties['public'] = data['pubkey']
ctx.instance.runtime_properties['base64private'] = data['data']
- # ctx.instance.runtime_properties['postgrespswd'] = getpass(data, 'postgres')
+ # not needed right now: ctx.instance.runtime_properties['postgrespswd'] = getpass(data, 'postgres', ctx.node.properties['writerfqdn'], 'postgres')
warn('All done')
except Exception as e:
ctx.logger.warn("Error: {0}".format(e))
@@ -515,3 +531,56 @@ def delete_database(**kwargs):
ctx.logger.warn("Error: {0}".format(e))
ctx.logger.warn("Stack: {0}".format(traceback.format_exc()))
raise e
+
+@operation
+def update_database(**kwargs):
+ """
+ dcae.nodes.pgaas.database:
+ Update the password for a database from a cluster
+ """
+ try:
+ debug("update_database() invoked")
+ dbname = ctx.node.properties['name']
+ warn("update_database({0})".format(safestr(dbname)))
+ if not chkdbname(dbname):
+ return
+ debug('update_database(): dbname checked out')
+ if ctx.node.properties['use_existing']:
+ return
+ debug('update_database(): !use_existing')
+ hostport = ctx.node.properties['writerfqdn']
+ debug('update_database(): wfqdn={}'.format(hostport))
+ info = dbgetinfo(ctx)
+ debug('Got db server info')
+ hostPortDbname = '{0}/pgaas/{1}:{2}'.format(OPT_MANAGER_RESOURCES, hostport.lower(), dbname.lower())
+ debug('update_database(): hostPortDbname={}'.format(hostPortDbname))
+ try:
+ appended = False
+ with open(hostPortDbname, "a") as fp:
+ with open("/dev/urandom", "rb") as rp:
+ b = rp.read(16)
+ import binascii
+ print(binascii.hexlify(b).decode('utf-8'), file=fp)
+ appended = True
+ if not appended:
+ ctx.logger.warn("Error: the password for {} {} was not successfully changed".format(hostport, dbname))
+ except Exception as e:
+ ctx.logger.warn("Error: {0}".format(e))
+ ctx.logger.warn("Stack: {0}".format(traceback.format_exc()))
+ raise e
+
+ with rootconn(info) as conn:
+ crx = conn.cursor()
+ admu = ctx.instance.runtime_properties['admin']['user']
+ usru = ctx.instance.runtime_properties['user']['user']
+ vwru = ctx.instance.runtime_properties['viewer']['user']
+ cusr = '{0}_common_user_role'.format(dbname)
+ cvwr = '{0}_common_viewer_role'.format(dbname)
+ for r in [ usru, vwru, admu ]:
+ dbexecute(crx,"ALTER USER {} WITH PASSWORD '{}'".format(r, getpass(info, r, hostport, dbname)))
+
+ warn('All users updated for database {}'.format(dbname))
+ except Exception as e:
+ ctx.logger.warn("Error: {0}".format(e))
+ ctx.logger.warn("Stack: {0}".format(traceback.format_exc()))
+ raise e
diff --git a/pgaas/pgaas_types.yaml b/pgaas/pgaas_types.yaml
index d98d326..2118231 100644
--- a/pgaas/pgaas_types.yaml
+++ b/pgaas/pgaas_types.yaml
@@ -53,6 +53,7 @@ node_types:
cloudify.interfaces.lifecycle:
create: pgaas.pgaas.pgaas_plugin.create_database
delete: pgaas.pgaas.pgaas_plugin.delete_database
+ update: pgaas.pgaas.pgaas_plugin.update_database
relationships:
dcae.relationships.pgaas_cluster_uses_sshkeypair:
diff --git a/pgaas/tests/test_plugin.py b/pgaas/tests/test_plugin.py
index 197654e..d3c29b9 100644
--- a/pgaas/tests/test_plugin.py
+++ b/pgaas/tests/test_plugin.py
@@ -31,6 +31,8 @@ from cloudify import ctx
import sys, os
sys.path.append(os.path.realpath(os.path.dirname(__file__)))
+TMPNAME = "/tmp/pgaas_plugin_tests"
+
class MockKeyPair(object):
def __init__(self, type_hierarchy=None, target=None):
self._type_hierarchy = type_hierarchy
@@ -74,7 +76,7 @@ def _connect(h,p):
def set_mock_context(msg, monkeypatch):
print("================ %s ================" % msg)
- os.system("echo Before test; ls -l /tmp/pgaas") #### DELETE
+ os.system("exec >> {0}.out 2>&1; echo Before test".format(TMPNAME)) #### DELETE
props = {
'writerfqdn': 'test.bar.example.com',
'use_existing': False,
@@ -106,18 +108,22 @@ def set_mock_context(msg, monkeypatch):
current_ctx.set(mock_ctx)
monkeypatch.setattr(socket.socket, 'connect', _connect)
# monkeypatch.setattr(psycopg2, 'connect', _connect)
- pgaas.pgaas_plugin.setOptManagerResources("/tmp")
+ pgaas.pgaas_plugin.setOptManagerResources(TMPNAME)
@pytest.mark.dependency()
+def test_start(monkeypatch):
+ os.system("exec > {0}.out 2>&1; echo Before any test; rm -rf {0}; mkdir -p {0}".format(TMPNAME)) #### DELETE
+
+@pytest.mark.dependency(depends=['test_start'])
def test_add_pgaas_cluster(monkeypatch):
try:
set_mock_context('test_add_pgaas_cluster', monkeypatch)
pgaas.pgaas_plugin.add_pgaas_cluster(args={})
finally:
current_ctx.clear()
- os.system("echo After test; ls -l /tmp/pgaas") #### DELETE
+ os.system("exec >> {0}.out 2>&1; echo After add_pgaas_cluster test; ls -lR {0}; head -1000 /dev/null {0}/pgaas/*;echo".format(TMPNAME)) #### DELETE
@pytest.mark.dependency(depends=['test_add_pgaas_cluster'])
def test_add_database(monkeypatch):
@@ -126,16 +132,25 @@ def test_add_database(monkeypatch):
pgaas.pgaas_plugin.create_database(args={})
finally:
current_ctx.clear()
- os.system("echo After test; ls -l /tmp/pgaas") #### DELETE
+ os.system("exec >> {0}.out 2>&1; echo After add_database test; ls -lR {0}; head -1000 /dev/null {0}/pgaas/*;echo".format(TMPNAME)) #### DELETE
@pytest.mark.dependency(depends=['test_add_database'])
+def test_update_database(monkeypatch):
+ try:
+ set_mock_context('test_update_database', monkeypatch)
+ pgaas.pgaas_plugin.update_database(args={})
+ finally:
+ current_ctx.clear()
+ os.system("exec >> {0}.out 2>&1; echo After update_database test; ls -lR {0}; head -1000 /dev/null {0}/pgaas/*;echo".format(TMPNAME)) #### DELETE
+
+@pytest.mark.dependency(depends=['test_update_database'])
def test_delete_database(monkeypatch):
try:
set_mock_context('test_delete_database', monkeypatch)
pgaas.pgaas_plugin.delete_database(args={})
finally:
current_ctx.clear()
- os.system("echo After test; ls -l /tmp/pgaas") #### DELETE
+ os.system("exec >> {0}.out 2>&1; echo After delete_database test; ls -lR {0}; head -1000 /dev/null {0}/pgaas/*;echo".format(TMPNAME)) #### DELETE
@pytest.mark.dependency(depends=['test_delete_database'])
def test_rm_pgaas_cluster(monkeypatch):
@@ -144,5 +159,5 @@ def test_rm_pgaas_cluster(monkeypatch):
pgaas.pgaas_plugin.rm_pgaas_cluster(args={})
finally:
current_ctx.clear()
- os.system("echo After test; ls -l /tmp/pgaas") #### DELETE
+ os.system("exec >> {0}.out 2>&1; echo After delete_database test; ls -lR {0}; head -1000 /dev/null {0}/pgaas/*;echo".format(TMPNAME)) #### DELETE