Merge ~msaikia/cloud-init:topic-msaikia-vmware into cloud-init:master

Proposed by Maitreyee Saikia
Status: Merged
Approved by: Chad Smith
Approved revision: d9e02b9b1d777445c2a8151f56fa3a1a3ba35e1a
Merge reported by: Chad Smith
Merged at revision: d9e02b9b1d777445c2a8151f56fa3a1a3ba35e1a
Proposed branch: ~msaikia/cloud-init:topic-msaikia-vmware
Merge into: cloud-init:master
Diff against target: 280 lines (+180/-6)
4 files modified
cloudinit/sources/DataSourceOVF.py (+62/-1)
cloudinit/sources/helpers/vmware/imc/config.py (+21/-3)
cloudinit/sources/helpers/vmware/imc/config_passwd.py (+67/-0)
tests/unittests/test_vmware_config_file.py (+30/-2)
Reviewer Review Type Date Requested Status
Chad Smith Approve
Server Team CI bot continuous-integration Approve
Scott Moser Pending
Review via email: mp+322991@code.launchpad.net

This proposal supersedes a proposal from 2016-09-12.

Commit message

vcloud directoy: Guest Customization support for passwords

This feature enables the following VMware VCloud Director functionalities:
1. Setting admin password
2. Expire password.
3. Set admin password and expire.
Password configuration is triggered only as part of a full recustomization, that happens either on first power on or when "poweron and full recustomization" is selected. Full customization flow is determined by marker files. Unique marker ids are generated when full recustomization is requested. And marker file based on these marker ids help to determine if we need to execute the above configuration.

Description of the change

1. Retrieve values from configuration file.
2. Parse data and call password customization.
3. Unit tests for retrieved password data

To post a comment you must log in.
Revision history for this message
Joshua Harlow (harlowja) : Posted in a previous version of this proposal
Revision history for this message
Maitreyee Saikia (msaikia) wrote : Posted in a previous version of this proposal

Thanks for the comments Joshua.
Updated the diff.

Revision history for this message
Maitreyee Saikia (msaikia) wrote : Posted in a previous version of this proposal

Updated some formatting.

Revision history for this message
Scott Moser (smoser) wrote : Posted in a previous version of this proposal

Please set a combined commit message ('Set commit message' above.

Can you explain where you're going also?
I'm concerned about adding more "vmware" paths for configuring things that are done elsewhere (or not elsewhere) in cloud-init. We'd like have consistent paths for doing things as much as possible.

I understand that you're trying to have cloud-init take over another more solution, and I'm not entirely opposed to that, but I want to integrate as much as possible rather than having specific paths and function on vmware.

Revision history for this message
Maitreyee Saikia (msaikia) wrote : Posted in a previous version of this proposal

> Please set a combined commit message ('Set commit message' above.
>
> Can you explain where you're going also?
> I'm concerned about adding more "vmware" paths for configuring things that are
> done elsewhere (or not elsewhere) in cloud-init. We'd like have consistent
> paths for doing things as much as possible.
>
> I understand that you're trying to have cloud-init take over another more
> solution, and I'm not entirely opposed to that, but I want to integrate as
> much as possible rather than having specific paths and function on vmware.

Thanks for your input Scott. I have updated the commit message.
This changeset is just to retrieve data from customization spec, for implementing some other customization functionalities like setting password, running post customization scripts etc. I shall post another changeset with the password configurator, which manipulates the values retrieved from these methods to be used by cloud-init's cc_set_password. We will be using as much cloud-init code as possible. For other functionalities like running pre and post customization specs that will be uploaded by the user, I dont think there is existing support. Please let me know if that sounds ok. Also for functions that are "vmware" specific, shall we still keep it in the helper path that we have created for vmware, or do we want to move them to some common code area?
Thanks
Maitreyee

Revision history for this message
Maitreyee Saikia (msaikia) : Posted in a previous version of this proposal
Revision history for this message
Server Team CI bot (server-team-bot) wrote : Posted in a previous version of this proposal
review: Needs Fixing (continuous-integration)
Revision history for this message
Server Team CI bot (server-team-bot) wrote : Posted in a previous version of this proposal
review: Approve (continuous-integration)
Revision history for this message
Scott Moser (smoser) wrote : Posted in a previous version of this proposal

There is one small nitpick in line.
There isn't anything wrong with this merge proposal, but it
a.) doesn't really do anything
  I realize you're planning on using these things going forward, and thats fine, but there is no reason to accept this MP until we also see that further work. So just add it here. You can rebase/squahs your commits into single function items in a "series" if that helps you.

b.) should add some unit tests.
unit tests will help stop others from breaking your code path inadvertently.

review: Needs Fixing
Revision history for this message
Scott Moser (smoser) wrote : Posted in a previous version of this proposal

Hi. I'm going to put this in "work in progress".
See the comments above. When youv'e addressed them, move it back to Needs Review.
Thanks
Scott

Revision history for this message
Server Team CI bot (server-team-bot) wrote :
review: Approve (continuous-integration)
Revision history for this message
Maitreyee Saikia (msaikia) wrote : Posted in a previous version of this proposal

Hi Scott
I have resubmitted my review request after addressing your comments.
https://code.launchpad.net/~msaikia/cloud-init/+git/cloud-init/+merge/322991
Please let me know if you get a chance to review the new changeset.

Thanks
Maitreyee

-----Original Message-----
From: <email address hidden> [mailto:<email address hidden>] On Behalf Of Scott Moser
Sent: Tuesday, March 14, 2017 7:25 AM
To: Maitreyee Saikia <email address hidden>
Subject: Re: [Merge] ~msaikia/cloud-init:topic-msaikia-vmware into cloud-init:master

Hi. I'm going to put this in "work in progress".
See the comments above. When youv'e addressed them, move it back to Needs Review.
Thanks
Scott
--
https://code.launchpad.net/~msaikia/cloud-init/+git/cloud-init/+merge/305427
You are the owner of ~msaikia/cloud-init:topic-msaikia-vmware.

Revision history for this message
Scott Moser (smoser) wrote :

Your commit message is probably out of date. Update that please.
Missing from it is at very least a mention of 'markerfile'. I have a feeling you're doing something that might be already done in util.FileSemaphores.

Thats just a quick review.
There are probably some other things.

Thanks!
Scott

a884268... by Maitreyee Saikia

Review comments

Revision history for this message
Server Team CI bot (server-team-bot) wrote :
review: Approve (continuous-integration)
Revision history for this message
Maitreyee Saikia (msaikia) wrote :

> Your commit message is probably out of date. Update that please.
> Missing from it is at very least a mention of 'markerfile'. I have a feeling
> you're doing something that might be already done in util.FileSemaphores.
>
> Thats just a quick review.
> There are probably some other things.
>
> Thanks!
> Scott

Thanks Scott for the reviews. I have updated the changeset and commit message with your comments. As for the Markerfile, it does not have anything to do with semaphores. Here is an explanation of what our marker file is used for. vCloud Director creates a new marker id every time a full customization is requested. And since a new marker id is generated and we do not have the corresponding marker file as yet, we go forward and execute password configurator and then create the marker file in order that this code execution doesnot happen in future unless a new marker id is generated (new marker id is generated only on first power on or when "power on and force recustomization" is selected). So, I do not feel we can use FileSemaphores here.

Revision history for this message
Scott Moser (smoser) wrote :

i think i'm fine with this. one tiny comment in line.

a080ca6... by Maitreyee Saikia

Addressed review comment to use warning when unable to reset password

Revision history for this message
Maitreyee Saikia (msaikia) wrote :

> i think i'm fine with this. one tiny comment in line.
Addressed. Thanks.

Revision history for this message
Server Team CI bot (server-team-bot) wrote :

FAILED: Continuous integration, rev:db962f2e31d9e6acb44828ee27b51a1a9b85a3e3
https://jenkins.ubuntu.com/server/job/cloud-init-ci/115/
Executed test runs:
    SUCCESS: Checkout
    FAILED: Unit & Style Tests

Click here to trigger a rebuild:
https://jenkins.ubuntu.com/server/job/cloud-init-ci/115/rebuild

review: Needs Fixing (continuous-integration)
Revision history for this message
Server Team CI bot (server-team-bot) wrote :

FAILED: Continuous integration, rev:f9a1fa4f56cde70faf7755c6285ccf61a9a176df
https://jenkins.ubuntu.com/server/job/cloud-init-ci/117/
Executed test runs:
    SUCCESS: Checkout
    FAILED: Unit & Style Tests

Click here to trigger a rebuild:
https://jenkins.ubuntu.com/server/job/cloud-init-ci/117/rebuild

review: Needs Fixing (continuous-integration)
Revision history for this message
Ryan Harper (raharper) wrote :

I believe you need to rebase to master to resolve this issue. You'll likely need to push --force your branch here to overwrite the history because of the rebase.

0531d33... by Maitreyee Saikia

Fix tox errors

2395779... by Maitreyee Saikia

Pull from remote

3da515e... by Maitreyee Saikia

merge conflicts

Revision history for this message
Server Team CI bot (server-team-bot) wrote :

FAILED: Continuous integration, rev:3da515eeaaf5bd591aeee7b950b37b7553af0fcc
https://jenkins.ubuntu.com/server/job/cloud-init-ci/128/
Executed test runs:
    SUCCESS: Checkout
    SUCCESS: Unit & Style Tests
    SUCCESS: Ubuntu LTS: Build
    SUCCESS: Ubuntu LTS: Integration
    FAILED: CentOS 6 & 7: Build & Test

Click here to trigger a rebuild:
https://jenkins.ubuntu.com/server/job/cloud-init-ci/128/rebuild

review: Needs Fixing (continuous-integration)
Revision history for this message
Chad Smith (chad.smith) wrote :

Thanks for the submit and pinging us. Here's a brief review of your code. Most and nits, feel free to object if you feel strongly one way or another. Again thanks for your time on this. I'll watch for updates to see where we get.

4cec8b6... by Maitreyee Saikia

Addressed Chad's review comments

Revision history for this message
Maitreyee Saikia (msaikia) :
Revision history for this message
Server Team CI bot (server-team-bot) wrote :

FAILED: Continuous integration, rev:4cec8b64ce655496e2df894bc3b742571f116a8d
https://jenkins.ubuntu.com/server/job/cloud-init-ci/129/
Executed test runs:
    SUCCESS: Checkout
    SUCCESS: Unit & Style Tests
    SUCCESS: Ubuntu LTS: Build
    SUCCESS: Ubuntu LTS: Integration
    FAILED: CentOS 6 & 7: Build & Test

Click here to trigger a rebuild:
https://jenkins.ubuntu.com/server/job/cloud-init-ci/129/rebuild

review: Needs Fixing (continuous-integration)
d9e02b9... by Maitreyee Saikia

Fixed CI test in cloud-init for Centos 6/7 tests

Revision history for this message
Server Team CI bot (server-team-bot) wrote :

PASSED: Continuous integration, rev:d9e02b9b1d777445c2a8151f56fa3a1a3ba35e1a
https://jenkins.ubuntu.com/server/job/cloud-init-ci/130/
Executed test runs:
    SUCCESS: Checkout
    SUCCESS: Unit & Style Tests
    SUCCESS: Ubuntu LTS: Build
    SUCCESS: Ubuntu LTS: Integration
    SUCCESS: CentOS 6 & 7: Build & Test
    IN_PROGRESS: Declarative: Post Actions

Click here to trigger a rebuild:
https://jenkins.ubuntu.com/server/job/cloud-init-ci/130/rebuild

review: Approve (continuous-integration)
Revision history for this message
Chad Smith (chad.smith) wrote :

I'm +1 with this changeset, thanks for the good work.

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1diff --git a/cloudinit/sources/DataSourceOVF.py b/cloudinit/sources/DataSourceOVF.py
2index f20c9a6..73d3877 100644
3--- a/cloudinit/sources/DataSourceOVF.py
4+++ b/cloudinit/sources/DataSourceOVF.py
5@@ -25,6 +25,8 @@ from cloudinit.sources.helpers.vmware.imc.config_file \
6 import ConfigFile
7 from cloudinit.sources.helpers.vmware.imc.config_nic \
8 import NicConfigurator
9+from cloudinit.sources.helpers.vmware.imc.config_passwd \
10+ import PasswordConfigurator
11 from cloudinit.sources.helpers.vmware.imc.guestcust_error \
12 import GuestCustErrorEnum
13 from cloudinit.sources.helpers.vmware.imc.guestcust_event \
14@@ -117,6 +119,8 @@ class DataSourceOVF(sources.DataSource):
15 (md, ud, cfg) = read_vmware_imc(conf)
16 dirpath = os.path.dirname(vmwareImcConfigFilePath)
17 nics = get_nics_to_enable(dirpath)
18+ markerid = conf.marker_id
19+ markerexists = check_marker_exists(markerid)
20 except Exception as e:
21 LOG.debug("Error parsing the customization Config File")
22 LOG.exception(e)
23@@ -127,7 +131,6 @@ class DataSourceOVF(sources.DataSource):
24 return False
25 finally:
26 util.del_dir(os.path.dirname(vmwareImcConfigFilePath))
27-
28 try:
29 LOG.debug("Applying the Network customization")
30 nicConfigurator = NicConfigurator(conf.nics)
31@@ -140,6 +143,35 @@ class DataSourceOVF(sources.DataSource):
32 GuestCustEventEnum.GUESTCUST_EVENT_NETWORK_SETUP_FAILED)
33 enable_nics(nics)
34 return False
35+ if markerid and not markerexists:
36+ LOG.debug("Applying password customization")
37+ pwdConfigurator = PasswordConfigurator()
38+ adminpwd = conf.admin_password
39+ try:
40+ resetpwd = conf.reset_password
41+ if adminpwd or resetpwd:
42+ pwdConfigurator.configure(adminpwd, resetpwd,
43+ self.distro)
44+ else:
45+ LOG.debug("Changing password is not needed")
46+ except Exception as e:
47+ LOG.debug("Error applying Password Configuration: %s", e)
48+ set_customization_status(
49+ GuestCustStateEnum.GUESTCUST_STATE_RUNNING,
50+ GuestCustEventEnum.GUESTCUST_EVENT_CUSTOMIZE_FAILED)
51+ enable_nics(nics)
52+ return False
53+ if markerid:
54+ LOG.debug("Handle marker creation")
55+ try:
56+ setup_marker_files(markerid)
57+ except Exception as e:
58+ LOG.debug("Error creating marker files: %s", e)
59+ set_customization_status(
60+ GuestCustStateEnum.GUESTCUST_STATE_RUNNING,
61+ GuestCustEventEnum.GUESTCUST_EVENT_CUSTOMIZE_FAILED)
62+ enable_nics(nics)
63+ return False
64
65 vmwarePlatformFound = True
66 set_customization_status(
67@@ -445,4 +477,33 @@ datasources = (
68 def get_datasource_list(depends):
69 return sources.list_from_depends(depends, datasources)
70
71+
72+# To check if marker file exists
73+def check_marker_exists(markerid):
74+ """
75+ Check the existence of a marker file.
76+ Presence of marker file determines whether a certain code path is to be
77+ executed. It is needed for partial guest customization in VMware.
78+ """
79+ if not markerid:
80+ return False
81+ markerfile = "/.markerfile-" + markerid
82+ if os.path.exists(markerfile):
83+ return True
84+ return False
85+
86+
87+# Create a marker file
88+def setup_marker_files(markerid):
89+ """
90+ Create a new marker file.
91+ Marker files are unique to a full customization workflow in VMware
92+ environment.
93+ """
94+ if not markerid:
95+ return
96+ markerfile = "/.markerfile-" + markerid
97+ util.del_file("/.markerfile-*.txt")
98+ open(markerfile, 'w').close()
99+
100 # vi: ts=4 expandtab
101diff --git a/cloudinit/sources/helpers/vmware/imc/config.py b/cloudinit/sources/helpers/vmware/imc/config.py
102index 9a5e3a8..49d441d 100644
103--- a/cloudinit/sources/helpers/vmware/imc/config.py
104+++ b/cloudinit/sources/helpers/vmware/imc/config.py
105@@ -5,6 +5,7 @@
106 #
107 # This file is part of cloud-init. See LICENSE file for license information.
108
109+
110 from .nic import Nic
111
112
113@@ -14,13 +15,16 @@ class Config(object):
114 Specification file.
115 """
116
117+ CUSTOM_SCRIPT = 'CUSTOM-SCRIPT|SCRIPT-NAME'
118 DNS = 'DNS|NAMESERVER|'
119- SUFFIX = 'DNS|SUFFIX|'
120+ DOMAINNAME = 'NETWORK|DOMAINNAME'
121+ HOSTNAME = 'NETWORK|HOSTNAME'
122+ MARKERID = 'MISC|MARKER-ID'
123 PASS = 'PASSWORD|-PASS'
124+ RESETPASS = 'PASSWORD|RESET'
125+ SUFFIX = 'DNS|SUFFIX|'
126 TIMEZONE = 'DATETIME|TIMEZONE'
127 UTC = 'DATETIME|UTC'
128- HOSTNAME = 'NETWORK|HOSTNAME'
129- DOMAINNAME = 'NETWORK|DOMAINNAME'
130
131 def __init__(self, configFile):
132 self._configFile = configFile
133@@ -82,4 +86,18 @@ class Config(object):
134
135 return res
136
137+ @property
138+ def reset_password(self):
139+ """Retreives if the root password needs to be reset."""
140+ resetPass = self._configFile.get(Config.RESETPASS, 'no')
141+ resetPass = resetPass.lower()
142+ if resetPass not in ('yes', 'no'):
143+ raise ValueError('ResetPassword value should be yes/no')
144+ return resetPass == 'yes'
145+
146+ @property
147+ def marker_id(self):
148+ """Returns marker id."""
149+ return self._configFile.get(Config.MARKERID, None)
150+
151 # vi: ts=4 expandtab
152diff --git a/cloudinit/sources/helpers/vmware/imc/config_passwd.py b/cloudinit/sources/helpers/vmware/imc/config_passwd.py
153new file mode 100644
154index 0000000..75cfbaa
155--- /dev/null
156+++ b/cloudinit/sources/helpers/vmware/imc/config_passwd.py
157@@ -0,0 +1,67 @@
158+# Copyright (C) 2016 Canonical Ltd.
159+# Copyright (C) 2016 VMware INC.
160+#
161+# Author: Maitreyee Saikia <msaikia@vmware.com>
162+#
163+# This file is part of cloud-init. See LICENSE file for license information.
164+
165+
166+import logging
167+import os
168+
169+from cloudinit import util
170+
171+LOG = logging.getLogger(__name__)
172+
173+
174+class PasswordConfigurator(object):
175+ """
176+ Class for changing configurations related to passwords in a VM. Includes
177+ setting and expiring passwords.
178+ """
179+ def configure(self, passwd, resetPasswd, distro):
180+ """
181+ Main method to perform all functionalities based on configuration file
182+ inputs.
183+ @param passwd: encoded admin password.
184+ @param resetPasswd: boolean to determine if password needs to be reset.
185+ @return cfg: dict to be used by cloud-init set_passwd code.
186+ """
187+ LOG.info('Starting password configuration')
188+ if passwd:
189+ passwd = util.b64d(passwd)
190+ allRootUsers = []
191+ for line in open('/etc/passwd', 'r'):
192+ if line.split(':')[2] == '0':
193+ allRootUsers.append(line.split(':')[0])
194+ # read shadow file and check for each user, if its uid0 or root.
195+ uidUsersList = []
196+ for line in open('/etc/shadow', 'r'):
197+ user = line.split(':')[0]
198+ if user in allRootUsers:
199+ uidUsersList.append(user)
200+ if passwd:
201+ LOG.info('Setting admin password')
202+ distro.set_passwd('root', passwd)
203+ if resetPasswd:
204+ self.reset_password(uidUsersList)
205+ LOG.info('Configure Password completed!')
206+
207+ def reset_password(self, uidUserList):
208+ """
209+ Method to reset password. Use passwd --expire command. Use chage if
210+ not succeeded using passwd command. Log failure message otherwise.
211+ @param: list of users for which to expire password.
212+ """
213+ LOG.info('Expiring password.')
214+ for user in uidUserList:
215+ try:
216+ out, err = util.subp(['passwd', '--expire', user])
217+ except util.ProcessExecutionError as e:
218+ if os.path.exists('/usr/bin/chage'):
219+ out, e = util.subp(['chage', '-d', '0', user])
220+ else:
221+ LOG.warning('Failed to expire password for %s with error: '
222+ '%s', user, e)
223+
224+# vi: ts=4 expandtab
225diff --git a/tests/unittests/test_vmware_config_file.py b/tests/unittests/test_vmware_config_file.py
226index 18475f1..03b36d3 100644
227--- a/tests/unittests/test_vmware_config_file.py
228+++ b/tests/unittests/test_vmware_config_file.py
229@@ -7,8 +7,8 @@
230
231 import logging
232 import sys
233-import unittest
234
235+from .helpers import CiTestCase
236 from cloudinit.sources.helpers.vmware.imc.boot_proto import BootProtoEnum
237 from cloudinit.sources.helpers.vmware.imc.config import Config
238 from cloudinit.sources.helpers.vmware.imc.config_file import ConfigFile
239@@ -17,7 +17,7 @@ logging.basicConfig(level=logging.DEBUG, stream=sys.stdout)
240 logger = logging.getLogger(__name__)
241
242
243-class TestVmwareConfigFile(unittest.TestCase):
244+class TestVmwareConfigFile(CiTestCase):
245
246 def test_utility_methods(self):
247 cf = ConfigFile("tests/data/vmware/cust-dhcp-2nic.cfg")
248@@ -90,4 +90,32 @@ class TestVmwareConfigFile(unittest.TestCase):
249 self.assertEqual('00:50:56:a6:8c:08', nics[0].mac, "mac0")
250 self.assertEqual(BootProtoEnum.DHCP, nics[0].bootProto, "bootproto0")
251
252+ def test_config_password(self):
253+ cf = ConfigFile("tests/data/vmware/cust-dhcp-2nic.cfg")
254+
255+ cf._insertKey("PASSWORD|-PASS", "test-password")
256+ cf._insertKey("PASSWORD|RESET", "no")
257+
258+ conf = Config(cf)
259+ self.assertEqual('test-password', conf.admin_password, "password")
260+ self.assertFalse(conf.reset_password, "do not reset password")
261+
262+ def test_config_reset_passwd(self):
263+ cf = ConfigFile("tests/data/vmware/cust-dhcp-2nic.cfg")
264+
265+ cf._insertKey("PASSWORD|-PASS", "test-password")
266+ cf._insertKey("PASSWORD|RESET", "random")
267+
268+ conf = Config(cf)
269+ with self.assertRaises(ValueError):
270+ conf.reset_password()
271+
272+ cf.clear()
273+ cf._insertKey("PASSWORD|RESET", "yes")
274+ self.assertEqual(1, len(cf), "insert size")
275+
276+ conf = Config(cf)
277+ self.assertTrue(conf.reset_password, "reset password")
278+
279+
280 # vi: ts=4 expandtab

Subscribers

People subscribed via source and target branches