Merge ~msaikia/cloud-init:topic-msaikia-vmware into cloud-init:master
- Git
- lp:~msaikia/cloud-init
- topic-msaikia-vmware
- Merge into master
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) |
Related bugs: |
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
Joshua Harlow (harlowja) : Posted in a previous version of this proposal | # |
Maitreyee Saikia (msaikia) wrote : Posted in a previous version of this proposal | # |
Maitreyee Saikia (msaikia) wrote : Posted in a previous version of this proposal | # |
Updated some formatting.
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.
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
Maitreyee Saikia (msaikia) : Posted in a previous version of this proposal | # |
Server Team CI bot (server-team-bot) wrote : Posted in a previous version of this proposal | # |
FAILED: Continuous integration, rev:312808680eb
https:/
Executed test runs:
SUCCESS: https:/
FAILURE: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
Click here to trigger a rebuild:
https:/
Server Team CI bot (server-team-bot) wrote : Posted in a previous version of this proposal | # |
PASSED: Continuous integration, rev:7c480f71a20
https:/
Executed test runs:
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
Click here to trigger a rebuild:
https:/
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.
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
Server Team CI bot (server-team-bot) wrote : | # |
PASSED: Continuous integration, rev:c886370ebec
https:/
Executed test runs:
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
Click here to trigger a rebuild:
https:/
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:/
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/
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:/
You are the owner of ~msaikia/
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.FileSemaph
Thats just a quick review.
There are probably some other things.
Thanks!
Scott
- a884268... by Maitreyee Saikia
-
Review comments
Server Team CI bot (server-team-bot) wrote : | # |
PASSED: Continuous integration, rev:324eb7e3ad0
https:/
Executed test runs:
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
Click here to trigger a rebuild:
https:/
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.FileSemaph
>
> 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.
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
Maitreyee Saikia (msaikia) wrote : | # |
> i think i'm fine with this. one tiny comment in line.
Addressed. Thanks.
Server Team CI bot (server-team-bot) wrote : | # |
FAILED: Continuous integration, rev:db962f2e31d
https:/
Executed test runs:
SUCCESS: Checkout
FAILED: Unit & Style Tests
Click here to trigger a rebuild:
https:/
Server Team CI bot (server-team-bot) wrote : | # |
FAILED: Continuous integration, rev:f9a1fa4f56c
https:/
Executed test runs:
SUCCESS: Checkout
FAILED: Unit & Style Tests
Click here to trigger a rebuild:
https:/
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
Server Team CI bot (server-team-bot) wrote : | # |
FAILED: Continuous integration, rev:3da515eeaaf
https:/
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:/
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
Maitreyee Saikia (msaikia) : | # |
Server Team CI bot (server-team-bot) wrote : | # |
FAILED: Continuous integration, rev:4cec8b64ce6
https:/
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:/
- d9e02b9... by Maitreyee Saikia
-
Fixed CI test in cloud-init for Centos 6/7 tests
Server Team CI bot (server-team-bot) wrote : | # |
PASSED: Continuous integration, rev:d9e02b9b1d7
https:/
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:/
Chad Smith (chad.smith) wrote : | # |
I'm +1 with this changeset, thanks for the good work.
Preview Diff
1 | diff --git a/cloudinit/sources/DataSourceOVF.py b/cloudinit/sources/DataSourceOVF.py |
2 | index 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 |
101 | diff --git a/cloudinit/sources/helpers/vmware/imc/config.py b/cloudinit/sources/helpers/vmware/imc/config.py |
102 | index 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 |
152 | diff --git a/cloudinit/sources/helpers/vmware/imc/config_passwd.py b/cloudinit/sources/helpers/vmware/imc/config_passwd.py |
153 | new file mode 100644 |
154 | index 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 |
225 | diff --git a/tests/unittests/test_vmware_config_file.py b/tests/unittests/test_vmware_config_file.py |
226 | index 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 |
Thanks for the comments Joshua.
Updated the diff.