Merge lp:~thedac/charm-helpers/os-snaps into lp:charm-helpers

Proposed by David Ames
Status: Merged
Merged at revision: 763
Proposed branch: lp:~thedac/charm-helpers/os-snaps
Merge into: lp:charm-helpers
Diff against target: 358 lines (+171/-11)
5 files modified
charmhelpers/contrib/openstack/context.py (+6/-0)
charmhelpers/contrib/openstack/utils.py (+81/-2)
charmhelpers/fetch/snap.py (+6/-0)
charmhelpers/fetch/ubuntu.py (+1/-0)
tests/contrib/openstack/test_openstack_utils.py (+77/-9)
To merge this branch: bzr merge lp:~thedac/charm-helpers/os-snaps
Reviewer Review Type Date Requested Status
Alex Kavanagh Approve
Corey Bryant (community) Approve
Review via email: mp+325702@code.launchpad.net

Description of the change

Helpers for install OpenStack snaps in OpenStack charms

To post a comment you must log in.
Revision history for this message
Alex Kavanagh (ajkavanagh) wrote :

Please see inline comments.

Also, are there no tests for the new functions? I see patches to some of the existing tests for patching out the snaps existence test, but not for the new functions.

(I picked 'needs information' rather than 'needs fixing' because I'm not sure about the design aspect of passing in the post_snap function).

review: Needs Information
lp:~thedac/charm-helpers/os-snaps updated
754. By Jorge Niedbalski

[freyes, r=niedbalski,ajkavanagh] Invoke apt-get only if there
missing packages.

755. By Jorge Niedbalski

[freyes,r=niedbalski,ajkavanagh] Fix for test_is_ipv6_disabled.

Revision history for this message
David Ames (thedac) wrote :

Alex,

Thanks for your suggestions.

I wrote the unit tests first which made make the changes simpler ;)

Fixed pretty much everything you requested.

lp:~thedac/charm-helpers/os-snaps updated
756. By Jorge Niedbalski

[niedbalski, r=freyes,ajkavanagh] Partial fix required for LP:#1699565.

757. By Jorge Niedbalski

[billy-olsen, r=niedbalski] Ensure the config_flags_parser returns an
OrderedDict.

758. By Tim Van Steenburgh

v0.17.0

Revision history for this message
Corey Bryant (corey.bryant) wrote :

Hi David, I have a few comments below. I think the major thing that is messing things up now is that the current snaps aren't published to a track like they all will be in the future. I'll look into publishing them to the edge channel of the ocata track.

Revision history for this message
David Ames (thedac) wrote :

Corey,

I see your point about tracks. Do let me know when you have the snaps published to both the track and the channel and I'll get this MP ready to handle it.

lp:~thedac/charm-helpers/os-snaps updated
759. By Stuart Bishop

[peter-sabaini,r=stub] Helper module for bcache devices

Revision history for this message
David Ames (thedac) wrote :

Corey,

I have responded inline on where we are at with your concerns. The next commit fixes the typos.

Revision history for this message
Corey Bryant (corey.bryant) wrote :

LGTM. I think we should move ahead with what David has here. Once we get tracks enabled [1] we can land track support in charm-helpers.

[1] https://forum.snapcraft.io/t/track-request-for-openstack-core-snaps/1282

Revision history for this message
Corey Bryant (corey.bryant) :
review: Approve
lp:~thedac/charm-helpers/os-snaps updated
760. By Jorge Niedbalski

[freyes, r=niedbalski] Add support to change a user's password in MySQL.

761. By Edward Hope-Morley

[zhhuabj,r=ajkavanagh]

When OpenStack clouds get bigger, more messaging transactions
will happen, which will cause more load on rabbitmq server. In
order to mitigate this, polling_interval rpc_response_timeout
and report_interval values are important to adjust accordingly
to the size of OpenStack cluster.

So this patch supports setting those values in neutron-api
centrally, then:

1. polling_interval

   neutron-openvswitch charm gets polling_interval via it's
   relations and set it in [agent] of ml2_conf.ini or
   openvswitch_agent.ini(>=Mitaka)

2. rpc_response_timeout

   neutron-gateway charm gets rpc_response_timeout via it's
   relations and set it in [default] of neutron.conf

3. report_interval

   Both neutron-openvswitch charm and neutron-gateway charm
   get report_interval via it's relations and set it in
   [agent] of neutron.conf

Thus we need to modifty charm-helpers to also set those values
in NeutronAPIContext.

Related-Bug: 1685788

Revision history for this message
Alex Kavanagh (ajkavanagh) wrote :

Looks good. Just a single comment for food for thought in the future.

review: Approve
lp:~thedac/charm-helpers/os-snaps updated
762. By James Page

Fix release name typos in CLOUD_ARCHIVE_POCKETS

763. By David Ames

[thedac, r=coreycb,tinwood] OpenStack Charm snap helpers

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'charmhelpers/contrib/openstack/context.py'
2--- charmhelpers/contrib/openstack/context.py 2017-05-01 05:08:50 +0000
3+++ charmhelpers/contrib/openstack/context.py 2017-07-12 15:52:52 +0000
4@@ -97,6 +97,7 @@
5 git_determine_usr_bin,
6 git_determine_python_path,
7 enable_memcache,
8+ snap_install_requested,
9 )
10 from charmhelpers.core.unitdata import kv
11
12@@ -244,6 +245,11 @@
13 'database_password': rdata.get(password_setting),
14 'database_type': 'mysql'
15 }
16+ # Note(coreycb): We can drop mysql+pymysql if we want when the
17+ # following review lands, though it seems mysql+pymysql would
18+ # be preferred. https://review.openstack.org/#/c/462190/
19+ if snap_install_requested():
20+ ctxt['database_type'] = 'mysql+pymysql'
21 if self.context_complete(ctxt):
22 db_ssl(rdata, ctxt, self.ssl_dir)
23 return ctxt
24
25=== modified file 'charmhelpers/contrib/openstack/utils.py'
26--- charmhelpers/contrib/openstack/utils.py 2017-06-29 22:46:34 +0000
27+++ charmhelpers/contrib/openstack/utils.py 2017-07-12 15:52:52 +0000
28@@ -51,6 +51,7 @@
29 status_set,
30 hook_name,
31 application_version_set,
32+ cached,
33 )
34
35 from charmhelpers.core.strutils import BasicStringComparator
36@@ -90,6 +91,12 @@
37 GPGKeyError,
38 get_upstream_version
39 )
40+
41+from charmhelpers.fetch.snap import (
42+ snap_install,
43+ SNAP_CHANNELS,
44+)
45+
46 from charmhelpers.contrib.storage.linux.utils import is_block_device, zap_disk
47 from charmhelpers.contrib.storage.linux.loopback import ensure_loopback_device
48 from charmhelpers.contrib.openstack.exceptions import OSContextError
49@@ -327,8 +334,10 @@
50 return ca_rel
51
52 # Best guess match based on deb string provided
53- if src.startswith('deb') or src.startswith('ppa'):
54- for k, v in six.iteritems(OPENSTACK_CODENAMES):
55+ if (src.startswith('deb') or
56+ src.startswith('ppa') or
57+ src.startswith('snap')):
58+ for v in OPENSTACK_CODENAMES.values():
59 if v in src:
60 return v
61
62@@ -397,6 +406,10 @@
63
64 def get_os_codename_package(package, fatal=True):
65 '''Derive OpenStack release codename from an installed package.'''
66+
67+ if snap_install_requested():
68+ return None
69+
70 import apt_pkg as apt
71
72 cache = apt_cache()
73@@ -610,6 +623,10 @@
74 a newer version of package.
75 """
76
77+ # TODO make upgrades snap aware
78+ if snap_install_requested():
79+ return False
80+
81 import apt_pkg as apt
82 src = config('openstack-origin')
83 cur_vers = get_os_version_package(package)
84@@ -2016,3 +2033,65 @@
85 policy.update(items)
86 with open(filename, "w") as fd:
87 fd.write(json.dumps(policy, indent=4))
88+
89+
90+@cached
91+def snap_install_requested():
92+ """ Determine if installing from snaps
93+
94+ If openstack-origin is of the form snap:channel-series-release
95+ and channel is in SNAPS_CHANNELS return True.
96+ """
97+ origin = config('openstack-origin')
98+ if not origin.startswith('snap:'):
99+ return False
100+
101+ _src = origin[5:]
102+ channel, series, release = _src.split('-')
103+ if channel.lower() in SNAP_CHANNELS:
104+ return True
105+ return False
106+
107+
108+def get_snaps_install_info_from_origin(snaps, src, mode='classic'):
109+ """Generate a dictionary of snap install information from origin
110+
111+ @param snaps: List of snaps
112+ @param src: String of openstack-origin or source of the form
113+ snap:channel-series-release
114+ @param mode: String classic, devmode or jailmode
115+ @returns: Dictionary of snaps with channels and modes
116+ """
117+
118+ if not src.startswith('snap:'):
119+ juju_log("Snap source is not a snap origin", 'WARN')
120+ return {}
121+
122+ _src = src[5:]
123+ channel, series, release = _src.split('-')
124+
125+ return {snap: {'channel': channel, 'mode': mode}
126+ for snap in snaps}
127+
128+
129+def install_os_snaps(snaps):
130+ """Install OpenStack snaps from channel and with mode
131+
132+ @param snaps: Dictionary of snaps with channels and modes of the form:
133+ {'snap_name': {'channel': 'snap_channel',
134+ 'mode': 'snap_mode'}}
135+ Where channel a snapstore channel and mode is --classic, --devmode or
136+ --jailmode.
137+ @param post_snap_install: Callback function to run after snaps have been
138+ installed
139+ """
140+
141+ def _ensure_flag(flag):
142+ if flag.startswith('--'):
143+ return flag
144+ return '--{}'.format(flag)
145+
146+ for snap in snaps.keys():
147+ snap_install(snap,
148+ _ensure_flag(snaps[snap]['channel']),
149+ _ensure_flag(snaps[snap]['mode']))
150
151=== modified file 'charmhelpers/fetch/snap.py'
152--- charmhelpers/fetch/snap.py 2017-03-03 09:41:22 +0000
153+++ charmhelpers/fetch/snap.py 2017-07-12 15:52:52 +0000
154@@ -27,6 +27,12 @@
155 SNAP_NO_LOCK = 1 # The return code for "couldn't acquire lock" in Snap (hopefully this will be improved).
156 SNAP_NO_LOCK_RETRY_DELAY = 10 # Wait X seconds between Snap lock checks.
157 SNAP_NO_LOCK_RETRY_COUNT = 30 # Retry to acquire the lock X times.
158+SNAP_CHANNELS = [
159+ 'edge',
160+ 'beta',
161+ 'candidate',
162+ 'stable',
163+]
164
165
166 class CouldNotAcquireLockException(Exception):
167
168=== modified file 'charmhelpers/fetch/ubuntu.py'
169--- charmhelpers/fetch/ubuntu.py 2017-05-25 13:46:14 +0000
170+++ charmhelpers/fetch/ubuntu.py 2017-07-12 15:52:52 +0000
171@@ -364,6 +364,7 @@
172 (r"^cloud:(.*)-(.*)\/staging$", _add_cloud_staging),
173 (r"^cloud:(.*)-(.*)$", _add_cloud_distro_check),
174 (r"^cloud:(.*)$", _add_cloud_pocket),
175+ (r"^snap:.*-(.*)-(.*)$", _add_cloud_distro_check),
176 ])
177 if source is None:
178 source = ''
179
180=== modified file 'tests/contrib/openstack/test_openstack_utils.py'
181--- tests/contrib/openstack/test_openstack_utils.py 2017-04-10 17:51:42 +0000
182+++ tests/contrib/openstack/test_openstack_utils.py 2017-07-12 15:52:52 +0000
183@@ -8,6 +8,7 @@
184 from mock import MagicMock, patch, call
185
186 from charmhelpers.fetch import ubuntu as fetch
187+from charmhelpers.core.hookenv import flush
188 import charmhelpers.contrib.openstack.utils as openstack
189
190 import six
191@@ -292,8 +293,10 @@
192 def test_get_swift_codename_none(self):
193 self.assertEquals(openstack.get_swift_codename('1.2.3'), None)
194
195- def test_os_codename_from_package(self):
196+ @patch.object(openstack, 'snap_install_requested')
197+ def test_os_codename_from_package(self, mock_snap_install_requested):
198 """Test deriving OpenStack codename from an installed package"""
199+ mock_snap_install_requested.return_value = False
200 with patch('apt_pkg.Cache') as cache:
201 cache.return_value = self._apt_cache()
202 for pkg, vers in six.iteritems(FAKE_REPO):
203@@ -305,18 +308,24 @@
204 self.assertEquals(openstack.get_os_codename_package(pkg),
205 vers['os_release'])
206
207+ @patch.object(openstack, 'snap_install_requested')
208 @patch('charmhelpers.contrib.openstack.utils.error_out')
209- def test_os_codename_from_bad_package_version(self, mocked_error):
210+ def test_os_codename_from_bad_package_version(self, mocked_error,
211+ mock_snap_install_requested):
212 """Test deriving OpenStack codename for a poorly versioned package"""
213+ mock_snap_install_requested.return_value = False
214 with patch('apt_pkg.Cache') as cache:
215 cache.return_value = self._apt_cache()
216 openstack.get_os_codename_package('bad-version')
217 _e = ('Could not determine OpenStack codename for version 2200.1')
218 mocked_error.assert_called_with(_e)
219
220+ @patch.object(openstack, 'snap_install_requested')
221 @patch('charmhelpers.contrib.openstack.utils.error_out')
222- def test_os_codename_from_bad_package(self, mocked_error):
223+ def test_os_codename_from_bad_package(self, mocked_error,
224+ mock_snap_install_requested):
225 """Test deriving OpenStack codename from an unavailable package"""
226+ mock_snap_install_requested.return_value = False
227 with patch('apt_pkg.Cache') as cache:
228 cache.return_value = self._apt_cache()
229 try:
230@@ -329,8 +338,11 @@
231 'candidate: foo'
232 mocked_error.assert_called_with(e)
233
234- def test_os_codename_from_bad_package_nonfatal(self):
235+ @patch.object(openstack, 'snap_install_requested')
236+ def test_os_codename_from_bad_package_nonfatal(
237+ self, mock_snap_install_requested):
238 """Test OpenStack codename from an unavailable package is non-fatal"""
239+ mock_snap_install_requested.return_value = False
240 with patch('apt_pkg.Cache') as cache:
241 cache.return_value = self._apt_cache()
242 self.assertEquals(
243@@ -338,9 +350,12 @@
244 openstack.get_os_codename_package('foo', fatal=False)
245 )
246
247+ @patch.object(openstack, 'snap_install_requested')
248 @patch('charmhelpers.contrib.openstack.utils.error_out')
249- def test_os_codename_from_uninstalled_package(self, mock_error):
250+ def test_os_codename_from_uninstalled_package(self, mock_error,
251+ mock_snap_install_requested):
252 """Test OpenStack codename from an available but uninstalled pkg"""
253+ mock_snap_install_requested.return_value = False
254 with patch('apt_pkg.Cache') as cache:
255 cache.return_value = self._apt_cache()
256 try:
257@@ -351,8 +366,11 @@
258 'cinder-common')
259 mock_error.assert_called_with(e)
260
261- def test_os_codename_from_uninstalled_package_nonfatal(self):
262+ @patch.object(openstack, 'snap_install_requested')
263+ def test_os_codename_from_uninstalled_package_nonfatal(
264+ self, mock_snap_install_requested):
265 """Test OpenStack codename from avail uninstalled pkg is non fatal"""
266+ mock_snap_install_requested.return_value = False
267 with patch('apt_pkg.Cache') as cache:
268 cache.return_value = self._apt_cache()
269 self.assertEquals(
270@@ -360,9 +378,12 @@
271 openstack.get_os_codename_package('cinder-common', fatal=False)
272 )
273
274+ @patch.object(openstack, 'snap_install_requested')
275 @patch('charmhelpers.contrib.openstack.utils.error_out')
276- def test_os_version_from_package(self, mocked_error):
277+ def test_os_version_from_package(self, mocked_error,
278+ mock_snap_install_requested):
279 """Test deriving OpenStack version from an installed package"""
280+ mock_snap_install_requested.return_value = False
281 with patch('apt_pkg.Cache') as cache:
282 cache.return_value = self._apt_cache()
283 for pkg, vers in six.iteritems(FAKE_REPO):
284@@ -373,9 +394,12 @@
285 self.assertEquals(openstack.get_os_version_package(pkg),
286 vers['os_version'])
287
288+ @patch.object(openstack, 'snap_install_requested')
289 @patch('charmhelpers.contrib.openstack.utils.error_out')
290- def test_os_version_from_bad_package(self, mocked_error):
291+ def test_os_version_from_bad_package(self, mocked_error,
292+ mock_snap_install_requested):
293 """Test deriving OpenStack version from an uninstalled package"""
294+ mock_snap_install_requested.return_value = False
295 with patch('apt_pkg.Cache') as cache:
296 cache.return_value = self._apt_cache()
297 try:
298@@ -388,8 +412,11 @@
299 'candidate: foo'
300 mocked_error.assert_called_with(e)
301
302- def test_os_version_from_bad_package_nonfatal(self):
303+ @patch.object(openstack, 'snap_install_requested')
304+ def test_os_version_from_bad_package_nonfatal(
305+ self, mock_snap_install_requested):
306 """Test OpenStack version from an uninstalled package is non-fatal"""
307+ mock_snap_install_requested.return_value = False
308 with patch('apt_pkg.Cache') as cache:
309 cache.return_value = self._apt_cache()
310 self.assertEquals(
311@@ -1886,6 +1913,47 @@
312 openstack.os_application_version_set('cinder-common')
313 mock_application_version_set.assert_called_with('mitaka')
314
315+ @patch('charmhelpers.contrib.openstack.utils.config')
316+ def test_snap_install_requested(self, config):
317+ # Expect True
318+ flush('snap_install_requested')
319+ config.return_value = 'snap:edge-xenial-ocata'
320+ self.assertTrue(openstack.snap_install_requested())
321+ flush('snap_install_requested')
322+ config.return_value = 'snap:BETA-xenial-ocata'
323+ self.assertTrue(openstack.snap_install_requested())
324+ # Expect False
325+ flush('snap_install_requested')
326+ config.return_value = 'snap:None-xenial-ocata'
327+ self.assertFalse(openstack.snap_install_requested())
328+ flush('snap_install_requested')
329+ config.return_value = 'cloud:xenial-ocata'
330+ self.assertFalse(openstack.snap_install_requested())
331+
332+ def test_get_snaps_install_info_from_origin(self):
333+ snaps = ['os_project']
334+ mode = 'jailmode'
335+ src = 'snap:beta-xenial-ocata'
336+ expected = {snaps[0]: {'mode': mode,
337+ 'channel': 'beta'}}
338+ self.assertEqual(
339+ expected,
340+ openstack.get_snaps_install_info_from_origin(snaps, src,
341+ mode=mode))
342+
343+ @patch.object(openstack, 'snap_install')
344+ def test_install_os_snaps(self, mock_snap_install):
345+ snaps = ['os_project']
346+ mode = 'jailmode'
347+ src = 'snap:beta-xenial-ocata'
348+ post_install = MagicMock()
349+
350+ openstack.install_os_snaps(
351+ openstack.get_snaps_install_info_from_origin(
352+ snaps, src, mode=mode))
353+ mock_snap_install.assert_called_with(
354+ 'os_project', '--beta', '--jailmode')
355+
356
357 if __name__ == '__main__':
358 unittest.main()

Subscribers

People subscribed via source and target branches