summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--cloudinit/sources/DataSourceOVF.py63
-rw-r--r--cloudinit/sources/helpers/vmware/imc/config.py24
-rw-r--r--cloudinit/sources/helpers/vmware/imc/config_passwd.py67
-rw-r--r--tests/unittests/test_vmware_config_file.py32
4 files changed, 180 insertions, 6 deletions
diff --git a/cloudinit/sources/DataSourceOVF.py b/cloudinit/sources/DataSourceOVF.py
index f20c9a65..73d38771 100644
--- a/cloudinit/sources/DataSourceOVF.py
+++ b/cloudinit/sources/DataSourceOVF.py
@@ -25,6 +25,8 @@ from cloudinit.sources.helpers.vmware.imc.config_file \
import ConfigFile
from cloudinit.sources.helpers.vmware.imc.config_nic \
import NicConfigurator
+from cloudinit.sources.helpers.vmware.imc.config_passwd \
+ import PasswordConfigurator
from cloudinit.sources.helpers.vmware.imc.guestcust_error \
import GuestCustErrorEnum
from cloudinit.sources.helpers.vmware.imc.guestcust_event \
@@ -117,6 +119,8 @@ class DataSourceOVF(sources.DataSource):
(md, ud, cfg) = read_vmware_imc(conf)
dirpath = os.path.dirname(vmwareImcConfigFilePath)
nics = get_nics_to_enable(dirpath)
+ markerid = conf.marker_id
+ markerexists = check_marker_exists(markerid)
except Exception as e:
LOG.debug("Error parsing the customization Config File")
LOG.exception(e)
@@ -127,7 +131,6 @@ class DataSourceOVF(sources.DataSource):
return False
finally:
util.del_dir(os.path.dirname(vmwareImcConfigFilePath))
-
try:
LOG.debug("Applying the Network customization")
nicConfigurator = NicConfigurator(conf.nics)
@@ -140,6 +143,35 @@ class DataSourceOVF(sources.DataSource):
GuestCustEventEnum.GUESTCUST_EVENT_NETWORK_SETUP_FAILED)
enable_nics(nics)
return False
+ if markerid and not markerexists:
+ LOG.debug("Applying password customization")
+ pwdConfigurator = PasswordConfigurator()
+ adminpwd = conf.admin_password
+ try:
+ resetpwd = conf.reset_password
+ if adminpwd or resetpwd:
+ pwdConfigurator.configure(adminpwd, resetpwd,
+ self.distro)
+ else:
+ LOG.debug("Changing password is not needed")
+ except Exception as e:
+ LOG.debug("Error applying Password Configuration: %s", e)
+ set_customization_status(
+ GuestCustStateEnum.GUESTCUST_STATE_RUNNING,
+ GuestCustEventEnum.GUESTCUST_EVENT_CUSTOMIZE_FAILED)
+ enable_nics(nics)
+ return False
+ if markerid:
+ LOG.debug("Handle marker creation")
+ try:
+ setup_marker_files(markerid)
+ except Exception as e:
+ LOG.debug("Error creating marker files: %s", e)
+ set_customization_status(
+ GuestCustStateEnum.GUESTCUST_STATE_RUNNING,
+ GuestCustEventEnum.GUESTCUST_EVENT_CUSTOMIZE_FAILED)
+ enable_nics(nics)
+ return False
vmwarePlatformFound = True
set_customization_status(
@@ -445,4 +477,33 @@ datasources = (
def get_datasource_list(depends):
return sources.list_from_depends(depends, datasources)
+
+# To check if marker file exists
+def check_marker_exists(markerid):
+ """
+ Check the existence of a marker file.
+ Presence of marker file determines whether a certain code path is to be
+ executed. It is needed for partial guest customization in VMware.
+ """
+ if not markerid:
+ return False
+ markerfile = "/.markerfile-" + markerid
+ if os.path.exists(markerfile):
+ return True
+ return False
+
+
+# Create a marker file
+def setup_marker_files(markerid):
+ """
+ Create a new marker file.
+ Marker files are unique to a full customization workflow in VMware
+ environment.
+ """
+ if not markerid:
+ return
+ markerfile = "/.markerfile-" + markerid
+ util.del_file("/.markerfile-*.txt")
+ open(markerfile, 'w').close()
+
# vi: ts=4 expandtab
diff --git a/cloudinit/sources/helpers/vmware/imc/config.py b/cloudinit/sources/helpers/vmware/imc/config.py
index 9a5e3a8a..49d441db 100644
--- a/cloudinit/sources/helpers/vmware/imc/config.py
+++ b/cloudinit/sources/helpers/vmware/imc/config.py
@@ -5,6 +5,7 @@
#
# This file is part of cloud-init. See LICENSE file for license information.
+
from .nic import Nic
@@ -14,13 +15,16 @@ class Config(object):
Specification file.
"""
+ CUSTOM_SCRIPT = 'CUSTOM-SCRIPT|SCRIPT-NAME'
DNS = 'DNS|NAMESERVER|'
- SUFFIX = 'DNS|SUFFIX|'
+ DOMAINNAME = 'NETWORK|DOMAINNAME'
+ HOSTNAME = 'NETWORK|HOSTNAME'
+ MARKERID = 'MISC|MARKER-ID'
PASS = 'PASSWORD|-PASS'
+ RESETPASS = 'PASSWORD|RESET'
+ SUFFIX = 'DNS|SUFFIX|'
TIMEZONE = 'DATETIME|TIMEZONE'
UTC = 'DATETIME|UTC'
- HOSTNAME = 'NETWORK|HOSTNAME'
- DOMAINNAME = 'NETWORK|DOMAINNAME'
def __init__(self, configFile):
self._configFile = configFile
@@ -82,4 +86,18 @@ class Config(object):
return res
+ @property
+ def reset_password(self):
+ """Retreives if the root password needs to be reset."""
+ resetPass = self._configFile.get(Config.RESETPASS, 'no')
+ resetPass = resetPass.lower()
+ if resetPass not in ('yes', 'no'):
+ raise ValueError('ResetPassword value should be yes/no')
+ return resetPass == 'yes'
+
+ @property
+ def marker_id(self):
+ """Returns marker id."""
+ return self._configFile.get(Config.MARKERID, None)
+
# vi: ts=4 expandtab
diff --git a/cloudinit/sources/helpers/vmware/imc/config_passwd.py b/cloudinit/sources/helpers/vmware/imc/config_passwd.py
new file mode 100644
index 00000000..75cfbaaf
--- /dev/null
+++ b/cloudinit/sources/helpers/vmware/imc/config_passwd.py
@@ -0,0 +1,67 @@
+# Copyright (C) 2016 Canonical Ltd.
+# Copyright (C) 2016 VMware INC.
+#
+# Author: Maitreyee Saikia <msaikia@vmware.com>
+#
+# This file is part of cloud-init. See LICENSE file for license information.
+
+
+import logging
+import os
+
+from cloudinit import util
+
+LOG = logging.getLogger(__name__)
+
+
+class PasswordConfigurator(object):
+ """
+ Class for changing configurations related to passwords in a VM. Includes
+ setting and expiring passwords.
+ """
+ def configure(self, passwd, resetPasswd, distro):
+ """
+ Main method to perform all functionalities based on configuration file
+ inputs.
+ @param passwd: encoded admin password.
+ @param resetPasswd: boolean to determine if password needs to be reset.
+ @return cfg: dict to be used by cloud-init set_passwd code.
+ """
+ LOG.info('Starting password configuration')
+ if passwd:
+ passwd = util.b64d(passwd)
+ allRootUsers = []
+ for line in open('/etc/passwd', 'r'):
+ if line.split(':')[2] == '0':
+ allRootUsers.append(line.split(':')[0])
+ # read shadow file and check for each user, if its uid0 or root.
+ uidUsersList = []
+ for line in open('/etc/shadow', 'r'):
+ user = line.split(':')[0]
+ if user in allRootUsers:
+ uidUsersList.append(user)
+ if passwd:
+ LOG.info('Setting admin password')
+ distro.set_passwd('root', passwd)
+ if resetPasswd:
+ self.reset_password(uidUsersList)
+ LOG.info('Configure Password completed!')
+
+ def reset_password(self, uidUserList):
+ """
+ Method to reset password. Use passwd --expire command. Use chage if
+ not succeeded using passwd command. Log failure message otherwise.
+ @param: list of users for which to expire password.
+ """
+ LOG.info('Expiring password.')
+ for user in uidUserList:
+ try:
+ out, err = util.subp(['passwd', '--expire', user])
+ except util.ProcessExecutionError as e:
+ if os.path.exists('/usr/bin/chage'):
+ out, e = util.subp(['chage', '-d', '0', user])
+ else:
+ LOG.warning('Failed to expire password for %s with error: '
+ '%s', user, e)
+
+# vi: ts=4 expandtab
diff --git a/tests/unittests/test_vmware_config_file.py b/tests/unittests/test_vmware_config_file.py
index 18475f10..03b36d31 100644
--- a/tests/unittests/test_vmware_config_file.py
+++ b/tests/unittests/test_vmware_config_file.py
@@ -7,8 +7,8 @@
import logging
import sys
-import unittest
+from .helpers import CiTestCase
from cloudinit.sources.helpers.vmware.imc.boot_proto import BootProtoEnum
from cloudinit.sources.helpers.vmware.imc.config import Config
from cloudinit.sources.helpers.vmware.imc.config_file import ConfigFile
@@ -17,7 +17,7 @@ logging.basicConfig(level=logging.DEBUG, stream=sys.stdout)
logger = logging.getLogger(__name__)
-class TestVmwareConfigFile(unittest.TestCase):
+class TestVmwareConfigFile(CiTestCase):
def test_utility_methods(self):
cf = ConfigFile("tests/data/vmware/cust-dhcp-2nic.cfg")
@@ -90,4 +90,32 @@ class TestVmwareConfigFile(unittest.TestCase):
self.assertEqual('00:50:56:a6:8c:08', nics[0].mac, "mac0")
self.assertEqual(BootProtoEnum.DHCP, nics[0].bootProto, "bootproto0")
+ def test_config_password(self):
+ cf = ConfigFile("tests/data/vmware/cust-dhcp-2nic.cfg")
+
+ cf._insertKey("PASSWORD|-PASS", "test-password")
+ cf._insertKey("PASSWORD|RESET", "no")
+
+ conf = Config(cf)
+ self.assertEqual('test-password', conf.admin_password, "password")
+ self.assertFalse(conf.reset_password, "do not reset password")
+
+ def test_config_reset_passwd(self):
+ cf = ConfigFile("tests/data/vmware/cust-dhcp-2nic.cfg")
+
+ cf._insertKey("PASSWORD|-PASS", "test-password")
+ cf._insertKey("PASSWORD|RESET", "random")
+
+ conf = Config(cf)
+ with self.assertRaises(ValueError):
+ conf.reset_password()
+
+ cf.clear()
+ cf._insertKey("PASSWORD|RESET", "yes")
+ self.assertEqual(1, len(cf), "insert size")
+
+ conf = Config(cf)
+ self.assertTrue(conf.reset_password, "reset password")
+
+
# vi: ts=4 expandtab