Merge ~barryprice/charm-canonical-livepatch/+git/canonical-livepatch-charm:master into ~livepatch-charmers/charm-canonical-livepatch:master

Proposed by Barry Price
Status: Merged
Merge reported by: Barry Price
Merged at revision: 1bcbce669e1c6f9a1cd0f7c046937060ef77c73e
Proposed branch: ~barryprice/charm-canonical-livepatch/+git/canonical-livepatch-charm:master
Merge into: ~livepatch-charmers/charm-canonical-livepatch:master
Diff against target: 171 lines (+54/-26)
1 file modified
reactive/canonical_livepatch.py (+54/-26)
Reviewer Review Type Date Requested Status
Paul Gear (community) Approve
Stuart Bishop (community) Approve
Review via email: mp+331851@code.launchpad.net
To post a comment you must log in.
Revision history for this message
Barry Price (barryprice) wrote :
Revision history for this message
Barry Price (barryprice) wrote :
Revision history for this message
Stuart Bishop (stub) wrote :

I guess at least with livepatch being a snap we don't have to worry about supporting the old format. Might be worth letting them know that this output is being scraped and if they change it they break things.

The patch looks good. I see a lot of hookenv.log() calls logging errors at the default INFO level, which you might want to fix. I'm unsure how much it matters with current Juju.

review: Approve
Revision history for this message
Paul Gear (paulgear) wrote :

See Stuart's comments about errors. I think all but one need changing.

review: Needs Fixing
Revision history for this message
Stuart Bishop (stub) wrote :

logging errors doesn't cause the charm to fail.

948802a... by Barry Price

Address LP#1721915, use appropriate log levels

2674170... by Barry Price

Address LP#1721915, use appropriate log levels

Revision history for this message
Paul Gear (paulgear) wrote :

Some stylistic comments inline, but LGTM.

review: Approve
1bcbce6... by Barry Price

Use preferred return syntax

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
diff --git a/reactive/canonical_livepatch.py b/reactive/canonical_livepatch.py
index bb9d547..8b39eab 100644
--- a/reactive/canonical_livepatch.py
+++ b/reactive/canonical_livepatch.py
@@ -7,7 +7,6 @@ from subprocess import check_call, check_output, CalledProcessError
7from time import sleep7from time import sleep
8from os import path, uname8from os import path, uname
9from yaml import load, dump9from yaml import load, dump
10from platform import release
11from distutils.version import LooseVersion10from distutils.version import LooseVersion
1211
1312
@@ -42,7 +41,10 @@ def wait_for_path(file_path, timeout=30):
42 sleep(interval)41 sleep(interval)
43 seconds_waited += interval42 seconds_waited += interval
44 if seconds_waited >= timeout:43 if seconds_waited >= timeout:
45 hookenv.log('Giving up waiting for {}'.format(file_path))44 hookenv.log(
45 'Giving up waiting for {}'.format(file_path),
46 hookenv.ERROR
47 )
46 return48 return
47 return49 return
4850
@@ -57,37 +59,44 @@ def unit_update(status=None, message=None):
57 if status and message:59 if status and message:
58 hookenv.status_set(status, message)60 hookenv.status_set(status, message)
59 else:61 else:
60 hookenv.status_set('active', 'Effective kernel {}'.format(62 patch_details = get_patch_details()
61 get_equiv_kernel_version())63 hookenv.status_set(
64 'active',
65 'Running kernel {}, patchState: {}'.format(*patch_details)
62 )66 )
6367
6468
65def get_equiv_kernel_version():69def get_patch_details():
66 # default to actual running kernel70 kernel = patch_state = 'unknown'
67 version_string = release()
68 livepatch_status = ''
6971
70 cmd = ['/snap/bin/canonical-livepatch', 'status']72 cmd = ['/snap/bin/canonical-livepatch', 'status']
71 try:73 try:
72 livepatch_status = check_output(cmd, universal_newlines=True)74 livepatch_state = check_output(cmd, universal_newlines=True)
73 except CalledProcessError as e:75 except CalledProcessError as e:
74 hookenv.log('Unable to get status: {}'.format(str(e)))76 hookenv.log('Unable to get status: {}'.format(str(e)), hookenv.ERROR)
75 return version_string77 return kernel, patch_state
7678
77 # status will usually pass YAML (but not always!)79 # status will usually pass YAML (but not always!)
78 try:80 try:
79 status_yaml = load(livepatch_status)81 status_yaml = load(livepatch_state)
80 except Exception:82 except Exception:
81 hookenv.log('Unable to parse status yaml')83 hookenv.log('Unable to parse status yaml', hookenv.ERROR)
82 return version_string84 return kernel, patch_state
8385
84 # even if we got YAML, be paranoid86 # even if we got YAML, be paranoid
85 try:87 try:
86 version_string = status_yaml['kernel']88 for k in status_yaml['status']:
89 if k['running'] is True:
90 kernel = k['kernel']
91 patch_state = k['livepatch']['patchState']
87 except Exception:92 except Exception:
88 hookenv.log('Unable to find kernel line in status yaml')93 hookenv.log(
94 'Unable to find patching details in status yaml',
95 hookenv.ERROR
96 )
97 return kernel, patch_state
8998
90 return version_string99 return kernel, patch_state
91100
92101
93def get_yaml_if_exists(path_to_yaml):102def get_yaml_if_exists(path_to_yaml):
@@ -97,7 +106,8 @@ def get_yaml_if_exists(path_to_yaml):
97 conf_yaml = load(stream)106 conf_yaml = load(stream)
98 except Exception:107 except Exception:
99 hookenv.log(108 hookenv.log(
100 'Unable to load YAML from {}'.format(path_to_yaml)109 'Unable to load YAML from {}'.format(path_to_yaml),
110 hookenv.ERROR
101 )111 )
102 else:112 else:
103 conf_yaml = {}113 conf_yaml = {}
@@ -148,7 +158,7 @@ def restart_livepatch():
148 try:158 try:
149 check_call(cmd, universal_newlines=True)159 check_call(cmd, universal_newlines=True)
150 except CalledProcessError:160 except CalledProcessError:
151 hookenv.log('Failed to stop the service')161 hookenv.log('Failed to stop the service', hookenv.ERROR)
152162
153 # and now try to start it again, it may fail the first time!163 # and now try to start it again, it may fail the first time!
154 cmd = [164 cmd = [
@@ -161,7 +171,8 @@ def restart_livepatch():
161 check_call(cmd, universal_newlines=True)171 check_call(cmd, universal_newlines=True)
162 except CalledProcessError:172 except CalledProcessError:
163 hookenv.log(173 hookenv.log(
164 'Failed to start the service - waiting 5s and then trying again'174 'Failed to start the service - waiting 5s and then trying again',
175 hookenv.ERROR
165 )176 )
166 sleep(5)177 sleep(5)
167 check_call(cmd, universal_newlines=True)178 check_call(cmd, universal_newlines=True)
@@ -182,7 +193,10 @@ def activate_livepatch():
182 try:193 try:
183 check_output(cmd, universal_newlines=True)194 check_output(cmd, universal_newlines=True)
184 except CalledProcessError as e:195 except CalledProcessError as e:
185 hookenv.log('Unable to deactivate: {}'.format(str(e)))196 hookenv.log(
197 'Unable to deactivate: {}'.format(str(e)),
198 hookenv.ERROR
199 )
186200
187 cmd = [201 cmd = [
188 '/snap/bin/canonical-livepatch',202 '/snap/bin/canonical-livepatch',
@@ -193,7 +207,10 @@ def activate_livepatch():
193 try:207 try:
194 check_output(cmd, universal_newlines=True)208 check_output(cmd, universal_newlines=True)
195 except CalledProcessError as e:209 except CalledProcessError as e:
196 hookenv.log('Unable to activate: {}'.format(str(e)))210 hookenv.log(
211 'Unable to activate: {}'.format(str(e)),
212 hookenv.ERROR
213 )
197 remove_state('canonical-livepatch.active')214 remove_state('canonical-livepatch.active')
198 unit_update('blocked', 'Activation failed')215 unit_update('blocked', 'Activation failed')
199 else:216 else:
@@ -201,7 +218,8 @@ def activate_livepatch():
201 unit_update()218 unit_update()
202 else:219 else:
203 hookenv.log(220 hookenv.log(
204 'Unable to activate canonical-livepatch as no key has been set'221 'Unable to activate canonical-livepatch as no key has been set',
222 hookenv.ERROR
205 )223 )
206 remove_state('canonical-livepatch.active')224 remove_state('canonical-livepatch.active')
207 unit_update(225 unit_update(
@@ -214,10 +232,20 @@ def activate_livepatch():
214def livepatch_supported():232def livepatch_supported():
215 arch = uname()[4]233 arch = uname()[4]
216 opts = layer.options('snap')234 opts = layer.options('snap')
217 supported_archs = opts['canonical-livepatch'].pop('supported-architectures', None)235 supported_archs = opts['canonical-livepatch'].pop(
236 'supported-architectures',
237 None
238 )
218 if supported_archs and arch not in supported_archs:239 if supported_archs and arch not in supported_archs:
219 hookenv.log('Livepatch does not currently support this architecture: {}'.format(arch))240 hookenv.log(
220 unit_update('blocked', 'Architecture {} is not supported by livepatch'.format(arch))241 'Livepatch does not currently support {} architecture'.format(
242 arch
243 )
244 )
245 unit_update(
246 'blocked',
247 'Architecture {} is not supported by livepatch'.format(arch)
248 )
221 if is_container():249 if is_container():
222 hookenv.log('OS container detected, livepatch is not needed')250 hookenv.log('OS container detected, livepatch is not needed')
223 unit_update('blocked', 'Livepatch is not needed in OS containers')251 unit_update('blocked', 'Livepatch is not needed in OS containers')

Subscribers

People subscribed via source and target branches