Merge ~sankaraditya/cloud-init:vmware-customize-utc-time into cloud-init:master

Proposed by Sankar Tanguturi
Status: Work in progress
Proposed branch: ~sankaraditya/cloud-init:vmware-customize-utc-time
Merge into: cloud-init:master
Diff against target: 193 lines (+148/-0)
4 files modified
cloudinit/sources/DataSourceOVF.py (+4/-0)
cloudinit/sources/helpers/vmware/imc/guestcust_util.py (+34/-0)
cloudinit/util.py (+51/-0)
tests/unittests/test_util.py (+59/-0)
Reviewer Review Type Date Requested Status
Server Team CI bot continuous-integration Approve
Scott Moser Pending
Chad Smith Pending
Review via email: mp+331751@code.launchpad.net

Description of the change

[VMware Guest customization] Add UTC customization for guests.

In 'VMware Guest Customization' workflow, we need to set the
setting UTC=yes|no for debian guests in /etc/default/rcS file.

Added the support to read the UTC setting and make necessary
changes to /etc/default/rcS file.
Note: For now, only debian guests are customized. Will add the
support for other guests later.

Added an utility function which can be used by other modules.
Added few test cases.

To post a comment you must log in.
Revision history for this message
Server Team CI bot (server-team-bot) wrote :

PASSED: Continuous integration, rev:aa52f7754b96cadbec8468374bb6d79618d2b13e
https://jenkins.ubuntu.com/server/job/cloud-init-ci/376/
Executed test runs:
    SUCCESS: Checkout
    SUCCESS: Unit & Style Tests
    SUCCESS: Ubuntu LTS: Build
    SUCCESS: Ubuntu LTS: Integration
    SUCCESS: MAAS Compatability Testing
    IN_PROGRESS: Declarative: Post Actions

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

review: Approve (continuous-integration)
Revision history for this message
Sankar Tanguturi (sankaraditya) wrote :

Chad and Scott,

Can you please take a look and provide your valuable comments. This is not a high risk patch and should be simple and straight forward.

Thanks
Sankar.

Revision history for this message
Sankar Tanguturi (sankaraditya) wrote :

Chad and Scott,

Any update?

Thanks
Sankar.

Revision history for this message
Chad Smith (chad.smith) wrote :

Grabbing this today Sankar. Sorry for the delay

Revision history for this message
Chad Smith (chad.smith) wrote :

So, I'm a hestant on this branch because cloud-init already surfaces a cc_timezone module which allows users to set timezone. That module talks to <distro>.set_timezone which in turn calls <distro>._find_timezone_file.

Growing that functionality in vmware-specific modules seems to be duplicating the user-facing configuration that cloud-init timsurfaces. Why wouldn't vmware providie timzone: UTC as vendor data?

If you need something specific for debian, or other distros that isn't handled by the Distro class, I'd recommend extending the distro class specifically to get this functionality instead of having distro-specific logic down in cloudinit/sources/helpers/vmware/imc/guestcust_util.py.

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

marked this as work in progress. we'd like for this functionality (configuration what is stored int he hwclock) should be in common cloud-init.

Unmerged commits

aa52f77... by Sankar Tanguturi

  [VMware Guest customization] Add UTC customization for guests.

  In 'VMware Guest Customization' workflow, we need to set the
  settig UTC=yes|no for debian guests in /etc/default/rcS file.

  Added the support to read the UTC setting and make necessary
  changes to /etc/default/rcS file.
  Note: For now, only debian guests are customized. Will add the
  support for other guests later.

  Added an utility function which can be used by other modules.
  Added few test cases.

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 ccebf11..94a7683 100644
3--- a/cloudinit/sources/DataSourceOVF.py
4+++ b/cloudinit/sources/DataSourceOVF.py
5@@ -34,6 +34,7 @@ from cloudinit.sources.helpers.vmware.imc.guestcust_event \
6 from cloudinit.sources.helpers.vmware.imc.guestcust_state \
7 import GuestCustStateEnum
8 from cloudinit.sources.helpers.vmware.imc.guestcust_util import (
9+ customize_utc,
10 enable_nics,
11 get_nics_to_enable,
12 set_customization_status
13@@ -142,6 +143,9 @@ class DataSourceOVF(sources.DataSource):
14 True,
15 True,
16 self.distro.osfamily)
17+ LOG.debug("Customizing the UTC setting")
18+ customize_utc(self._vmware_cust_conf.utc,
19+ self.distro.osfamily)
20 except Exception as e:
21 LOG.exception(e)
22 set_customization_status(
23diff --git a/cloudinit/sources/helpers/vmware/imc/guestcust_util.py b/cloudinit/sources/helpers/vmware/imc/guestcust_util.py
24index 4407525..518d3d2 100644
25--- a/cloudinit/sources/helpers/vmware/imc/guestcust_util.py
26+++ b/cloudinit/sources/helpers/vmware/imc/guestcust_util.py
27@@ -117,4 +117,38 @@ def enable_nics(nics):
28 logger.warning("Can't connect network interfaces after %d attempts",
29 enableNicsWaitRetries)
30
31+
32+def customize_utc(utc, osfamily):
33+ """Customizes the UTC setting for the guest OS.
34+
35+ @param utc: utc setting to be set. It should be either 'yes' or 'no'
36+ @param osfamily: family of the guest OS.
37+ """
38+
39+ if not osfamily or osfamily != "debian":
40+ logger.info("Debian OS not detected. Skipping the customize step")
41+ return
42+
43+ if utc != "yes" and utc != "no":
44+ logger.info("Invalid UTC value: '%s' specified. Skipping.", utc)
45+ return
46+
47+ utc_file = "/etc/default/rcS"
48+ pattern = "UTC"
49+ replace_with = "UTC=" + utc
50+
51+ lines = []
52+ try:
53+ if os.path.isfile(utc_file):
54+ lines = util.load_file(utc_file).splitlines()
55+ else:
56+ util.ensure_file(utc_file)
57+ except (IOError, OSError):
58+ util.logexc(logger, "Error reading lines from %s", utc_file)
59+ return
60+
61+ new_lines = util.replace_or_append_in_lines(lines, pattern, replace_with)
62+
63+ util.write_file(utc_file, content='\n'.join(new_lines))
64+
65 # vi: ts=4 expandtab
66diff --git a/cloudinit/util.py b/cloudinit/util.py
67index e1290aa..410387a 100644
68--- a/cloudinit/util.py
69+++ b/cloudinit/util.py
70@@ -2541,4 +2541,55 @@ def load_shell_content(content, add_empty=False, empty_val=None):
71 return data
72
73
74+def get_line_without_comments(line):
75+ """Get the content from beginning to the first occurence of #
76+
77+ @param line: content that needs to be checked.
78+ """
79+ if line:
80+ tokens = line.split('#')
81+ return tokens[0]
82+ else:
83+ return line
84+
85+
86+def replace_or_append_in_lines(lines, pattern, replace_with,
87+ skip_comments=True):
88+ """Checks and replaces each line with the new content if
89+ if it matches with the specified pattern. If no line
90+ matches with the specified pattern, then a new line
91+ is added with the new content.
92+
93+ @param lines: An array of lines to be checked.
94+ @param pattern: regex pattern to check in each line.
95+ @param replace_with: New content to replace if the pattern
96+ is matched in the line.
97+ @param skip_comments: Whether to skip the comments or not.
98+ If this is true, then the pattern is
99+ not checked for the comments.
100+ """
101+ new_lines = []
102+ found = False
103+
104+ if not pattern or not replace_with:
105+ return new_lines
106+
107+ for line in lines:
108+ if skip_comments:
109+ new_content = get_line_without_comments(line)
110+ else:
111+ new_content = line
112+
113+ if re.search(pattern, new_content, re.IGNORECASE):
114+ new_lines.append(replace_with)
115+ found = True
116+ else:
117+ new_lines.append(line)
118+
119+ if not found:
120+ new_lines.append(replace_with)
121+
122+ return new_lines
123+
124+
125 # vi: ts=4 expandtab
126diff --git a/tests/unittests/test_util.py b/tests/unittests/test_util.py
127index 3e4154c..9e96d6d 100644
128--- a/tests/unittests/test_util.py
129+++ b/tests/unittests/test_util.py
130@@ -834,4 +834,63 @@ class TestLoadShellContent(helpers.TestCase):
131 ''])))
132
133
134+class TestReplaceAppendLines(helpers.TestCase):
135+ def test_get_line_without_comments(self):
136+ """Test get_line_without_comments function."""
137+ self.assertEqual("", util.get_line_without_comments(""))
138+ self.assertEqual("no comment",
139+ util.get_line_without_comments("no comment"))
140+ self.assertEqual("content ",
141+ util.get_line_without_comments("content # comment"))
142+ self.assertEqual("",
143+ util.get_line_without_comments("#comment"))
144+
145+ def test_replace_append_lines(self):
146+ """Test replace_or_append_in_lines function."""
147+ output = util.replace_or_append_in_lines(["content"], None,
148+ "replace_with")
149+ self.assertEqual([], output)
150+
151+ output = util.replace_or_append_in_lines(["content"], "pattern",
152+ None)
153+ self.assertEqual([], output)
154+
155+ output = util.replace_or_append_in_lines([], "pattern", "replace")
156+ self.assertEqual(["replace"], output)
157+
158+ input = ["#pattern", "pattern # comment", "content line"]
159+ pattern = "pattern"
160+ replace_with = "replaced"
161+
162+ skip_comments = True
163+ expected_output = ["#pattern",
164+ "replaced",
165+ "content line"]
166+
167+ output = util.replace_or_append_in_lines(input, pattern,
168+ replace_with, skip_comments)
169+
170+ self.assertEqual(expected_output, output)
171+
172+ skip_comments = False
173+ expected_output = ["replaced",
174+ "replaced",
175+ "content line"]
176+
177+ output = util.replace_or_append_in_lines(input, pattern,
178+ replace_with, skip_comments)
179+
180+ self.assertEqual(expected_output, output)
181+
182+ input = ["first line", "second line"]
183+ pattern = "pattern"
184+ replace_with = "replaced"
185+ expected_output = input[:]
186+ expected_output.append(replace_with)
187+
188+ output = util.replace_or_append_in_lines(input, pattern,
189+ replace_with, skip_comments)
190+ self.assertEqual(expected_output, output)
191+
192+
193 # vi: ts=4 expandtab

Subscribers

People subscribed via source and target branches