Merge lp:~hopem/charms/trusty/cinder/lp1499643 into lp:~openstack-charmers-archive/charms/trusty/cinder/next

Proposed by Edward Hope-Morley
Status: Merged
Merged at revision: 128
Proposed branch: lp:~hopem/charms/trusty/cinder/lp1499643
Merge into: lp:~openstack-charmers-archive/charms/trusty/cinder/next
Diff against target: 810 lines (+407/-53)
11 files modified
hooks/charmhelpers/contrib/network/ip.py (+5/-3)
hooks/charmhelpers/contrib/openstack/amulet/deployment.py (+9/-10)
hooks/charmhelpers/contrib/openstack/context.py (+52/-7)
hooks/charmhelpers/contrib/openstack/templating.py (+30/-2)
hooks/charmhelpers/contrib/openstack/utils.py (+181/-2)
hooks/charmhelpers/core/hookenv.py (+32/-0)
hooks/charmhelpers/core/hugepage.py (+8/-1)
hooks/charmhelpers/core/strutils.py (+30/-0)
tests/charmhelpers/contrib/amulet/deployment.py (+4/-2)
tests/charmhelpers/contrib/amulet/utils.py (+47/-16)
tests/charmhelpers/contrib/openstack/amulet/deployment.py (+9/-10)
To merge this branch: bzr merge lp:~hopem/charms/trusty/cinder/lp1499643
Reviewer Review Type Date Requested Status
Liam Young (community) Approve
Review via email: mp+272532@code.launchpad.net
To post a comment you must log in.
Revision history for this message
uosci-testing-bot (uosci-testing-bot) wrote :

charm_lint_check #10886 cinder-next for hopem mp272532
    LINT OK: passed

Build: http://10.245.162.77:8080/job/charm_lint_check/10886/

Revision history for this message
uosci-testing-bot (uosci-testing-bot) wrote :

charm_unit_test #10058 cinder-next for hopem mp272532
    UNIT OK: passed

Build: http://10.245.162.77:8080/job/charm_unit_test/10058/

Revision history for this message
Liam Young (gnuoy) wrote :

Approve

review: Approve
Revision history for this message
uosci-testing-bot (uosci-testing-bot) wrote :

charm_amulet_test #6841 cinder-next for hopem mp272532
    AMULET OK: passed

Build: http://10.245.162.77:8080/job/charm_amulet_test/6841/

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'hooks/charmhelpers/contrib/network/ip.py'
--- hooks/charmhelpers/contrib/network/ip.py 2015-09-03 09:43:34 +0000
+++ hooks/charmhelpers/contrib/network/ip.py 2015-09-27 18:46:57 +0000
@@ -23,7 +23,7 @@
23from functools import partial23from functools import partial
2424
25from charmhelpers.core.hookenv import unit_get25from charmhelpers.core.hookenv import unit_get
26from charmhelpers.fetch import apt_install26from charmhelpers.fetch import apt_install, apt_update
27from charmhelpers.core.hookenv import (27from charmhelpers.core.hookenv import (
28 log,28 log,
29 WARNING,29 WARNING,
@@ -32,13 +32,15 @@
32try:32try:
33 import netifaces33 import netifaces
34except ImportError:34except ImportError:
35 apt_install('python-netifaces')35 apt_update(fatal=True)
36 apt_install('python-netifaces', fatal=True)
36 import netifaces37 import netifaces
3738
38try:39try:
39 import netaddr40 import netaddr
40except ImportError:41except ImportError:
41 apt_install('python-netaddr')42 apt_update(fatal=True)
43 apt_install('python-netaddr', fatal=True)
42 import netaddr44 import netaddr
4345
4446
4547
=== modified file 'hooks/charmhelpers/contrib/openstack/amulet/deployment.py'
--- hooks/charmhelpers/contrib/openstack/amulet/deployment.py 2015-09-14 20:19:57 +0000
+++ hooks/charmhelpers/contrib/openstack/amulet/deployment.py 2015-09-27 18:46:57 +0000
@@ -58,19 +58,17 @@
58 else:58 else:
59 base_series = self.current_next59 base_series = self.current_next
6060
61 if self.stable:61 for svc in other_services:
62 for svc in other_services:62 if svc['name'] in force_series_current:
63 if svc['name'] in force_series_current:63 base_series = self.current_next
64 base_series = self.current_next64 # If a location has been explicitly set, use it
6565 if svc.get('location'):
66 continue
67 if self.stable:
66 temp = 'lp:charms/{}/{}'68 temp = 'lp:charms/{}/{}'
67 svc['location'] = temp.format(base_series,69 svc['location'] = temp.format(base_series,
68 svc['name'])70 svc['name'])
69 else:71 else:
70 for svc in other_services:
71 if svc['name'] in force_series_current:
72 base_series = self.current_next
73
74 if svc['name'] in base_charms:72 if svc['name'] in base_charms:
75 temp = 'lp:charms/{}/{}'73 temp = 'lp:charms/{}/{}'
76 svc['location'] = temp.format(base_series,74 svc['location'] = temp.format(base_series,
@@ -79,6 +77,7 @@
79 temp = 'lp:~openstack-charmers/charms/{}/{}/next'77 temp = 'lp:~openstack-charmers/charms/{}/{}/next'
80 svc['location'] = temp.format(self.current_next,78 svc['location'] = temp.format(self.current_next,
81 svc['name'])79 svc['name'])
80
82 return other_services81 return other_services
8382
84 def _add_services(self, this_service, other_services):83 def _add_services(self, this_service, other_services):
8584
=== modified file 'hooks/charmhelpers/contrib/openstack/context.py'
--- hooks/charmhelpers/contrib/openstack/context.py 2015-09-12 06:32:34 +0000
+++ hooks/charmhelpers/contrib/openstack/context.py 2015-09-27 18:46:57 +0000
@@ -194,10 +194,50 @@
194class OSContextGenerator(object):194class OSContextGenerator(object):
195 """Base class for all context generators."""195 """Base class for all context generators."""
196 interfaces = []196 interfaces = []
197 related = False
198 complete = False
199 missing_data = []
197200
198 def __call__(self):201 def __call__(self):
199 raise NotImplementedError202 raise NotImplementedError
200203
204 def context_complete(self, ctxt):
205 """Check for missing data for the required context data.
206 Set self.missing_data if it exists and return False.
207 Set self.complete if no missing data and return True.
208 """
209 # Fresh start
210 self.complete = False
211 self.missing_data = []
212 for k, v in six.iteritems(ctxt):
213 if v is None or v == '':
214 if k not in self.missing_data:
215 self.missing_data.append(k)
216
217 if self.missing_data:
218 self.complete = False
219 log('Missing required data: %s' % ' '.join(self.missing_data), level=INFO)
220 else:
221 self.complete = True
222 return self.complete
223
224 def get_related(self):
225 """Check if any of the context interfaces have relation ids.
226 Set self.related and return True if one of the interfaces
227 has relation ids.
228 """
229 # Fresh start
230 self.related = False
231 try:
232 for interface in self.interfaces:
233 if relation_ids(interface):
234 self.related = True
235 return self.related
236 except AttributeError as e:
237 log("{} {}"
238 "".format(self, e), 'INFO')
239 return self.related
240
201241
202class SharedDBContext(OSContextGenerator):242class SharedDBContext(OSContextGenerator):
203 interfaces = ['shared-db']243 interfaces = ['shared-db']
@@ -213,6 +253,7 @@
213 self.database = database253 self.database = database
214 self.user = user254 self.user = user
215 self.ssl_dir = ssl_dir255 self.ssl_dir = ssl_dir
256 self.rel_name = self.interfaces[0]
216257
217 def __call__(self):258 def __call__(self):
218 self.database = self.database or config('database')259 self.database = self.database or config('database')
@@ -246,6 +287,7 @@
246 password_setting = self.relation_prefix + '_password'287 password_setting = self.relation_prefix + '_password'
247288
248 for rid in relation_ids(self.interfaces[0]):289 for rid in relation_ids(self.interfaces[0]):
290 self.related = True
249 for unit in related_units(rid):291 for unit in related_units(rid):
250 rdata = relation_get(rid=rid, unit=unit)292 rdata = relation_get(rid=rid, unit=unit)
251 host = rdata.get('db_host')293 host = rdata.get('db_host')
@@ -257,7 +299,7 @@
257 'database_password': rdata.get(password_setting),299 'database_password': rdata.get(password_setting),
258 'database_type': 'mysql'300 'database_type': 'mysql'
259 }301 }
260 if context_complete(ctxt):302 if self.context_complete(ctxt):
261 db_ssl(rdata, ctxt, self.ssl_dir)303 db_ssl(rdata, ctxt, self.ssl_dir)
262 return ctxt304 return ctxt
263 return {}305 return {}
@@ -278,6 +320,7 @@
278320
279 ctxt = {}321 ctxt = {}
280 for rid in relation_ids(self.interfaces[0]):322 for rid in relation_ids(self.interfaces[0]):
323 self.related = True
281 for unit in related_units(rid):324 for unit in related_units(rid):
282 rel_host = relation_get('host', rid=rid, unit=unit)325 rel_host = relation_get('host', rid=rid, unit=unit)
283 rel_user = relation_get('user', rid=rid, unit=unit)326 rel_user = relation_get('user', rid=rid, unit=unit)
@@ -287,7 +330,7 @@
287 'database_user': rel_user,330 'database_user': rel_user,
288 'database_password': rel_passwd,331 'database_password': rel_passwd,
289 'database_type': 'postgresql'}332 'database_type': 'postgresql'}
290 if context_complete(ctxt):333 if self.context_complete(ctxt):
291 return ctxt334 return ctxt
292335
293 return {}336 return {}
@@ -348,6 +391,7 @@
348 ctxt['signing_dir'] = cachedir391 ctxt['signing_dir'] = cachedir
349392
350 for rid in relation_ids(self.rel_name):393 for rid in relation_ids(self.rel_name):
394 self.related = True
351 for unit in related_units(rid):395 for unit in related_units(rid):
352 rdata = relation_get(rid=rid, unit=unit)396 rdata = relation_get(rid=rid, unit=unit)
353 serv_host = rdata.get('service_host')397 serv_host = rdata.get('service_host')
@@ -366,7 +410,7 @@
366 'service_protocol': svc_protocol,410 'service_protocol': svc_protocol,
367 'auth_protocol': auth_protocol})411 'auth_protocol': auth_protocol})
368412
369 if context_complete(ctxt):413 if self.context_complete(ctxt):
370 # NOTE(jamespage) this is required for >= icehouse414 # NOTE(jamespage) this is required for >= icehouse
371 # so a missing value just indicates keystone needs415 # so a missing value just indicates keystone needs
372 # upgrading416 # upgrading
@@ -405,6 +449,7 @@
405 ctxt = {}449 ctxt = {}
406 for rid in relation_ids(self.rel_name):450 for rid in relation_ids(self.rel_name):
407 ha_vip_only = False451 ha_vip_only = False
452 self.related = True
408 for unit in related_units(rid):453 for unit in related_units(rid):
409 if relation_get('clustered', rid=rid, unit=unit):454 if relation_get('clustered', rid=rid, unit=unit):
410 ctxt['clustered'] = True455 ctxt['clustered'] = True
@@ -437,7 +482,7 @@
437 ha_vip_only = relation_get('ha-vip-only',482 ha_vip_only = relation_get('ha-vip-only',
438 rid=rid, unit=unit) is not None483 rid=rid, unit=unit) is not None
439484
440 if context_complete(ctxt):485 if self.context_complete(ctxt):
441 if 'rabbit_ssl_ca' in ctxt:486 if 'rabbit_ssl_ca' in ctxt:
442 if not self.ssl_dir:487 if not self.ssl_dir:
443 log("Charm not setup for ssl support but ssl ca "488 log("Charm not setup for ssl support but ssl ca "
@@ -469,7 +514,7 @@
469 ctxt['oslo_messaging_flags'] = config_flags_parser(514 ctxt['oslo_messaging_flags'] = config_flags_parser(
470 oslo_messaging_flags)515 oslo_messaging_flags)
471516
472 if not context_complete(ctxt):517 if not self.complete:
473 return {}518 return {}
474519
475 return ctxt520 return ctxt
@@ -507,7 +552,7 @@
507 if not os.path.isdir('/etc/ceph'):552 if not os.path.isdir('/etc/ceph'):
508 os.mkdir('/etc/ceph')553 os.mkdir('/etc/ceph')
509554
510 if not context_complete(ctxt):555 if not self.context_complete(ctxt):
511 return {}556 return {}
512557
513 ensure_packages(['ceph-common'])558 ensure_packages(['ceph-common'])
@@ -1366,6 +1411,6 @@
1366 'auth_protocol':1411 'auth_protocol':
1367 rdata.get('auth_protocol') or 'http',1412 rdata.get('auth_protocol') or 'http',
1368 }1413 }
1369 if context_complete(ctxt):1414 if self.context_complete(ctxt):
1370 return ctxt1415 return ctxt
1371 return {}1416 return {}
13721417
=== modified file 'hooks/charmhelpers/contrib/openstack/templating.py'
--- hooks/charmhelpers/contrib/openstack/templating.py 2015-07-29 10:49:28 +0000
+++ hooks/charmhelpers/contrib/openstack/templating.py 2015-09-27 18:46:57 +0000
@@ -18,7 +18,7 @@
1818
19import six19import six
2020
21from charmhelpers.fetch import apt_install21from charmhelpers.fetch import apt_install, apt_update
22from charmhelpers.core.hookenv import (22from charmhelpers.core.hookenv import (
23 log,23 log,
24 ERROR,24 ERROR,
@@ -29,6 +29,7 @@
29try:29try:
30 from jinja2 import FileSystemLoader, ChoiceLoader, Environment, exceptions30 from jinja2 import FileSystemLoader, ChoiceLoader, Environment, exceptions
31except ImportError:31except ImportError:
32 apt_update(fatal=True)
32 apt_install('python-jinja2', fatal=True)33 apt_install('python-jinja2', fatal=True)
33 from jinja2 import FileSystemLoader, ChoiceLoader, Environment, exceptions34 from jinja2 import FileSystemLoader, ChoiceLoader, Environment, exceptions
3435
@@ -112,7 +113,7 @@
112113
113 def complete_contexts(self):114 def complete_contexts(self):
114 '''115 '''
115 Return a list of interfaces that have atisfied contexts.116 Return a list of interfaces that have satisfied contexts.
116 '''117 '''
117 if self._complete_contexts:118 if self._complete_contexts:
118 return self._complete_contexts119 return self._complete_contexts
@@ -293,3 +294,30 @@
293 [interfaces.extend(i.complete_contexts())294 [interfaces.extend(i.complete_contexts())
294 for i in six.itervalues(self.templates)]295 for i in six.itervalues(self.templates)]
295 return interfaces296 return interfaces
297
298 def get_incomplete_context_data(self, interfaces):
299 '''
300 Return dictionary of relation status of interfaces and any missing
301 required context data. Example:
302 {'amqp': {'missing_data': ['rabbitmq_password'], 'related': True},
303 'zeromq-configuration': {'related': False}}
304 '''
305 incomplete_context_data = {}
306
307 for i in six.itervalues(self.templates):
308 for context in i.contexts:
309 for interface in interfaces:
310 related = False
311 if interface in context.interfaces:
312 related = context.get_related()
313 missing_data = context.missing_data
314 if missing_data:
315 incomplete_context_data[interface] = {'missing_data': missing_data}
316 if related:
317 if incomplete_context_data.get(interface):
318 incomplete_context_data[interface].update({'related': True})
319 else:
320 incomplete_context_data[interface] = {'related': True}
321 else:
322 incomplete_context_data[interface] = {'related': False}
323 return incomplete_context_data
296324
=== modified file 'hooks/charmhelpers/contrib/openstack/utils.py'
--- hooks/charmhelpers/contrib/openstack/utils.py 2015-09-14 20:19:57 +0000
+++ hooks/charmhelpers/contrib/openstack/utils.py 2015-09-27 18:46:57 +0000
@@ -42,7 +42,9 @@
42 charm_dir,42 charm_dir,
43 INFO,43 INFO,
44 relation_ids,44 relation_ids,
45 relation_set45 relation_set,
46 status_set,
47 hook_name
46)48)
4749
48from charmhelpers.contrib.storage.linux.lvm import (50from charmhelpers.contrib.storage.linux.lvm import (
@@ -52,7 +54,8 @@
52)54)
5355
54from charmhelpers.contrib.network.ip import (56from charmhelpers.contrib.network.ip import (
55 get_ipv6_addr57 get_ipv6_addr,
58 is_ipv6,
56)59)
5760
58from charmhelpers.contrib.python.packages import (61from charmhelpers.contrib.python.packages import (
@@ -517,6 +520,12 @@
517 relation_prefix=None):520 relation_prefix=None):
518 hosts = get_ipv6_addr(dynamic_only=False)521 hosts = get_ipv6_addr(dynamic_only=False)
519522
523 if config('vip'):
524 vips = config('vip').split()
525 for vip in vips:
526 if vip and is_ipv6(vip):
527 hosts.append(vip)
528
520 kwargs = {'database': database,529 kwargs = {'database': database,
521 'username': database_user,530 'username': database_user,
522 'hostname': json.dumps(hosts)}531 'hostname': json.dumps(hosts)}
@@ -754,6 +763,176 @@
754 return None763 return None
755764
756765
766def os_workload_status(configs, required_interfaces, charm_func=None):
767 """
768 Decorator to set workload status based on complete contexts
769 """
770 def wrap(f):
771 @wraps(f)
772 def wrapped_f(*args, **kwargs):
773 # Run the original function first
774 f(*args, **kwargs)
775 # Set workload status now that contexts have been
776 # acted on
777 set_os_workload_status(configs, required_interfaces, charm_func)
778 return wrapped_f
779 return wrap
780
781
782def set_os_workload_status(configs, required_interfaces, charm_func=None):
783 """
784 Set workload status based on complete contexts.
785 status-set missing or incomplete contexts
786 and juju-log details of missing required data.
787 charm_func is a charm specific function to run checking
788 for charm specific requirements such as a VIP setting.
789 """
790 incomplete_rel_data = incomplete_relation_data(configs, required_interfaces)
791 state = 'active'
792 missing_relations = []
793 incomplete_relations = []
794 message = None
795 charm_state = None
796 charm_message = None
797
798 for generic_interface in incomplete_rel_data.keys():
799 related_interface = None
800 missing_data = {}
801 # Related or not?
802 for interface in incomplete_rel_data[generic_interface]:
803 if incomplete_rel_data[generic_interface][interface].get('related'):
804 related_interface = interface
805 missing_data = incomplete_rel_data[generic_interface][interface].get('missing_data')
806 # No relation ID for the generic_interface
807 if not related_interface:
808 juju_log("{} relation is missing and must be related for "
809 "functionality. ".format(generic_interface), 'WARN')
810 state = 'blocked'
811 if generic_interface not in missing_relations:
812 missing_relations.append(generic_interface)
813 else:
814 # Relation ID exists but no related unit
815 if not missing_data:
816 # Edge case relation ID exists but departing
817 if ('departed' in hook_name() or 'broken' in hook_name()) \
818 and related_interface in hook_name():
819 state = 'blocked'
820 if generic_interface not in missing_relations:
821 missing_relations.append(generic_interface)
822 juju_log("{} relation's interface, {}, "
823 "relationship is departed or broken "
824 "and is required for functionality."
825 "".format(generic_interface, related_interface), "WARN")
826 # Normal case relation ID exists but no related unit
827 # (joining)
828 else:
829 juju_log("{} relations's interface, {}, is related but has "
830 "no units in the relation."
831 "".format(generic_interface, related_interface), "INFO")
832 # Related unit exists and data missing on the relation
833 else:
834 juju_log("{} relation's interface, {}, is related awaiting "
835 "the following data from the relationship: {}. "
836 "".format(generic_interface, related_interface,
837 ", ".join(missing_data)), "INFO")
838 if state != 'blocked':
839 state = 'waiting'
840 if generic_interface not in incomplete_relations \
841 and generic_interface not in missing_relations:
842 incomplete_relations.append(generic_interface)
843
844 if missing_relations:
845 message = "Missing relations: {}".format(", ".join(missing_relations))
846 if incomplete_relations:
847 message += "; incomplete relations: {}" \
848 "".format(", ".join(incomplete_relations))
849 state = 'blocked'
850 elif incomplete_relations:
851 message = "Incomplete relations: {}" \
852 "".format(", ".join(incomplete_relations))
853 state = 'waiting'
854
855 # Run charm specific checks
856 if charm_func:
857 charm_state, charm_message = charm_func(configs)
858 if charm_state != 'active' and charm_state != 'unknown':
859 state = workload_state_compare(state, charm_state)
860 if message:
861 message = "{} {}".format(message, charm_message)
862 else:
863 message = charm_message
864
865 # Set to active if all requirements have been met
866 if state == 'active':
867 message = "Unit is ready"
868 juju_log(message, "INFO")
869
870 status_set(state, message)
871
872
873def workload_state_compare(current_workload_state, workload_state):
874 """ Return highest priority of two states"""
875 hierarchy = {'unknown': -1,
876 'active': 0,
877 'maintenance': 1,
878 'waiting': 2,
879 'blocked': 3,
880 }
881
882 if hierarchy.get(workload_state) is None:
883 workload_state = 'unknown'
884 if hierarchy.get(current_workload_state) is None:
885 current_workload_state = 'unknown'
886
887 # Set workload_state based on hierarchy of statuses
888 if hierarchy.get(current_workload_state) > hierarchy.get(workload_state):
889 return current_workload_state
890 else:
891 return workload_state
892
893
894def incomplete_relation_data(configs, required_interfaces):
895 """
896 Check complete contexts against required_interfaces
897 Return dictionary of incomplete relation data.
898
899 configs is an OSConfigRenderer object with configs registered
900
901 required_interfaces is a dictionary of required general interfaces
902 with dictionary values of possible specific interfaces.
903 Example:
904 required_interfaces = {'database': ['shared-db', 'pgsql-db']}
905
906 The interface is said to be satisfied if anyone of the interfaces in the
907 list has a complete context.
908
909 Return dictionary of incomplete or missing required contexts with relation
910 status of interfaces and any missing data points. Example:
911 {'message':
912 {'amqp': {'missing_data': ['rabbitmq_password'], 'related': True},
913 'zeromq-configuration': {'related': False}},
914 'identity':
915 {'identity-service': {'related': False}},
916 'database':
917 {'pgsql-db': {'related': False},
918 'shared-db': {'related': True}}}
919 """
920 complete_ctxts = configs.complete_contexts()
921 incomplete_relations = []
922 for svc_type in required_interfaces.keys():
923 # Avoid duplicates
924 found_ctxt = False
925 for interface in required_interfaces[svc_type]:
926 if interface in complete_ctxts:
927 found_ctxt = True
928 if not found_ctxt:
929 incomplete_relations.append(svc_type)
930 incomplete_context_data = {}
931 for i in incomplete_relations:
932 incomplete_context_data[i] = configs.get_incomplete_context_data(required_interfaces[i])
933 return incomplete_context_data
934
935
757def do_action_openstack_upgrade(package, upgrade_callback, configs):936def do_action_openstack_upgrade(package, upgrade_callback, configs):
758 """Perform action-managed OpenStack upgrade.937 """Perform action-managed OpenStack upgrade.
759938
760939
=== modified file 'hooks/charmhelpers/core/hookenv.py'
--- hooks/charmhelpers/core/hookenv.py 2015-09-03 09:43:34 +0000
+++ hooks/charmhelpers/core/hookenv.py 2015-09-27 18:46:57 +0000
@@ -623,6 +623,38 @@
623 return unit_get('private-address')623 return unit_get('private-address')
624624
625625
626@cached
627def storage_get(attribute="", storage_id=""):
628 """Get storage attributes"""
629 _args = ['storage-get', '--format=json']
630 if storage_id:
631 _args.extend(('-s', storage_id))
632 if attribute:
633 _args.append(attribute)
634 try:
635 return json.loads(subprocess.check_output(_args).decode('UTF-8'))
636 except ValueError:
637 return None
638
639
640@cached
641def storage_list(storage_name=""):
642 """List the storage IDs for the unit"""
643 _args = ['storage-list', '--format=json']
644 if storage_name:
645 _args.append(storage_name)
646 try:
647 return json.loads(subprocess.check_output(_args).decode('UTF-8'))
648 except ValueError:
649 return None
650 except OSError as e:
651 import errno
652 if e.errno == errno.ENOENT:
653 # storage-list does not exist
654 return []
655 raise
656
657
626class UnregisteredHookError(Exception):658class UnregisteredHookError(Exception):
627 """Raised when an undefined hook is called"""659 """Raised when an undefined hook is called"""
628 pass660 pass
629661
=== modified file 'hooks/charmhelpers/core/hugepage.py'
--- hooks/charmhelpers/core/hugepage.py 2015-08-19 13:51:56 +0000
+++ hooks/charmhelpers/core/hugepage.py 2015-09-27 18:46:57 +0000
@@ -25,11 +25,13 @@
25 fstab_mount,25 fstab_mount,
26 mkdir,26 mkdir,
27)27)
28from charmhelpers.core.strutils import bytes_from_string
29from subprocess import check_output
2830
2931
30def hugepage_support(user, group='hugetlb', nr_hugepages=256,32def hugepage_support(user, group='hugetlb', nr_hugepages=256,
31 max_map_count=65536, mnt_point='/run/hugepages/kvm',33 max_map_count=65536, mnt_point='/run/hugepages/kvm',
32 pagesize='2MB', mount=True):34 pagesize='2MB', mount=True, set_shmmax=False):
33 """Enable hugepages on system.35 """Enable hugepages on system.
3436
35 Args:37 Args:
@@ -49,6 +51,11 @@
49 'vm.max_map_count': max_map_count,51 'vm.max_map_count': max_map_count,
50 'vm.hugetlb_shm_group': gid,52 'vm.hugetlb_shm_group': gid,
51 }53 }
54 if set_shmmax:
55 shmmax_current = int(check_output(['sysctl', '-n', 'kernel.shmmax']))
56 shmmax_minsize = bytes_from_string(pagesize) * nr_hugepages
57 if shmmax_minsize > shmmax_current:
58 sysctl_settings['kernel.shmmax'] = shmmax_minsize
52 sysctl.create(yaml.dump(sysctl_settings), '/etc/sysctl.d/10-hugepage.conf')59 sysctl.create(yaml.dump(sysctl_settings), '/etc/sysctl.d/10-hugepage.conf')
53 mkdir(mnt_point, owner='root', group='root', perms=0o755, force=False)60 mkdir(mnt_point, owner='root', group='root', perms=0o755, force=False)
54 lfstab = fstab.Fstab()61 lfstab = fstab.Fstab()
5562
=== modified file 'hooks/charmhelpers/core/strutils.py'
--- hooks/charmhelpers/core/strutils.py 2015-04-13 19:41:31 +0000
+++ hooks/charmhelpers/core/strutils.py 2015-09-27 18:46:57 +0000
@@ -18,6 +18,7 @@
18# along with charm-helpers. If not, see <http://www.gnu.org/licenses/>.18# along with charm-helpers. If not, see <http://www.gnu.org/licenses/>.
1919
20import six20import six
21import re
2122
2223
23def bool_from_string(value):24def bool_from_string(value):
@@ -40,3 +41,32 @@
4041
41 msg = "Unable to interpret string value '%s' as boolean" % (value)42 msg = "Unable to interpret string value '%s' as boolean" % (value)
42 raise ValueError(msg)43 raise ValueError(msg)
44
45
46def bytes_from_string(value):
47 """Interpret human readable string value as bytes.
48
49 Returns int
50 """
51 BYTE_POWER = {
52 'K': 1,
53 'KB': 1,
54 'M': 2,
55 'MB': 2,
56 'G': 3,
57 'GB': 3,
58 'T': 4,
59 'TB': 4,
60 'P': 5,
61 'PB': 5,
62 }
63 if isinstance(value, six.string_types):
64 value = six.text_type(value)
65 else:
66 msg = "Unable to interpret non-string value '%s' as boolean" % (value)
67 raise ValueError(msg)
68 matches = re.match("([0-9]+)([a-zA-Z]+)", value)
69 if not matches:
70 msg = "Unable to interpret string value '%s' as bytes" % (value)
71 raise ValueError(msg)
72 return int(matches.group(1)) * (1024 ** BYTE_POWER[matches.group(2)])
4373
=== modified file 'tests/charmhelpers/contrib/amulet/deployment.py'
--- tests/charmhelpers/contrib/amulet/deployment.py 2015-01-26 09:47:37 +0000
+++ tests/charmhelpers/contrib/amulet/deployment.py 2015-09-27 18:46:57 +0000
@@ -51,7 +51,8 @@
51 if 'units' not in this_service:51 if 'units' not in this_service:
52 this_service['units'] = 152 this_service['units'] = 1
5353
54 self.d.add(this_service['name'], units=this_service['units'])54 self.d.add(this_service['name'], units=this_service['units'],
55 constraints=this_service.get('constraints'))
5556
56 for svc in other_services:57 for svc in other_services:
57 if 'location' in svc:58 if 'location' in svc:
@@ -64,7 +65,8 @@
64 if 'units' not in svc:65 if 'units' not in svc:
65 svc['units'] = 166 svc['units'] = 1
6667
67 self.d.add(svc['name'], charm=branch_location, units=svc['units'])68 self.d.add(svc['name'], charm=branch_location, units=svc['units'],
69 constraints=svc.get('constraints'))
6870
69 def _add_relations(self, relations):71 def _add_relations(self, relations):
70 """Add all of the relations for the services."""72 """Add all of the relations for the services."""
7173
=== modified file 'tests/charmhelpers/contrib/amulet/utils.py'
--- tests/charmhelpers/contrib/amulet/utils.py 2015-09-14 20:19:57 +0000
+++ tests/charmhelpers/contrib/amulet/utils.py 2015-09-27 18:46:57 +0000
@@ -326,7 +326,7 @@
326326
327 def service_restarted_since(self, sentry_unit, mtime, service,327 def service_restarted_since(self, sentry_unit, mtime, service,
328 pgrep_full=None, sleep_time=20,328 pgrep_full=None, sleep_time=20,
329 retry_count=2, retry_sleep_time=30):329 retry_count=30, retry_sleep_time=10):
330 """Check if service was been started after a given time.330 """Check if service was been started after a given time.
331331
332 Args:332 Args:
@@ -334,8 +334,9 @@
334 mtime (float): The epoch time to check against334 mtime (float): The epoch time to check against
335 service (string): service name to look for in process table335 service (string): service name to look for in process table
336 pgrep_full: [Deprecated] Use full command line search mode with pgrep336 pgrep_full: [Deprecated] Use full command line search mode with pgrep
337 sleep_time (int): Seconds to sleep before looking for process337 sleep_time (int): Initial sleep time (s) before looking for file
338 retry_count (int): If service is not found, how many times to retry338 retry_sleep_time (int): Time (s) to sleep between retries
339 retry_count (int): If file is not found, how many times to retry
339340
340 Returns:341 Returns:
341 bool: True if service found and its start time it newer than mtime,342 bool: True if service found and its start time it newer than mtime,
@@ -359,11 +360,12 @@
359 pgrep_full)360 pgrep_full)
360 self.log.debug('Attempt {} to get {} proc start time on {} '361 self.log.debug('Attempt {} to get {} proc start time on {} '
361 'OK'.format(tries, service, unit_name))362 'OK'.format(tries, service, unit_name))
362 except IOError:363 except IOError as e:
363 # NOTE(beisner) - race avoidance, proc may not exist yet.364 # NOTE(beisner) - race avoidance, proc may not exist yet.
364 # https://bugs.launchpad.net/charm-helpers/+bug/1474030365 # https://bugs.launchpad.net/charm-helpers/+bug/1474030
365 self.log.debug('Attempt {} to get {} proc start time on {} '366 self.log.debug('Attempt {} to get {} proc start time on {} '
366 'failed'.format(tries, service, unit_name))367 'failed\n{}'.format(tries, service,
368 unit_name, e))
367 time.sleep(retry_sleep_time)369 time.sleep(retry_sleep_time)
368 tries += 1370 tries += 1
369371
@@ -383,35 +385,62 @@
383 return False385 return False
384386
385 def config_updated_since(self, sentry_unit, filename, mtime,387 def config_updated_since(self, sentry_unit, filename, mtime,
386 sleep_time=20):388 sleep_time=20, retry_count=30,
389 retry_sleep_time=10):
387 """Check if file was modified after a given time.390 """Check if file was modified after a given time.
388391
389 Args:392 Args:
390 sentry_unit (sentry): The sentry unit to check the file mtime on393 sentry_unit (sentry): The sentry unit to check the file mtime on
391 filename (string): The file to check mtime of394 filename (string): The file to check mtime of
392 mtime (float): The epoch time to check against395 mtime (float): The epoch time to check against
393 sleep_time (int): Seconds to sleep before looking for process396 sleep_time (int): Initial sleep time (s) before looking for file
397 retry_sleep_time (int): Time (s) to sleep between retries
398 retry_count (int): If file is not found, how many times to retry
394399
395 Returns:400 Returns:
396 bool: True if file was modified more recently than mtime, False if401 bool: True if file was modified more recently than mtime, False if
397 file was modified before mtime,402 file was modified before mtime, or if file not found.
398 """403 """
399 self.log.debug('Checking %s updated since %s' % (filename, mtime))404 unit_name = sentry_unit.info['unit_name']
405 self.log.debug('Checking that %s updated since %s on '
406 '%s' % (filename, mtime, unit_name))
400 time.sleep(sleep_time)407 time.sleep(sleep_time)
401 file_mtime = self._get_file_mtime(sentry_unit, filename)408 file_mtime = None
409 tries = 0
410 while tries <= retry_count and not file_mtime:
411 try:
412 file_mtime = self._get_file_mtime(sentry_unit, filename)
413 self.log.debug('Attempt {} to get {} file mtime on {} '
414 'OK'.format(tries, filename, unit_name))
415 except IOError as e:
416 # NOTE(beisner) - race avoidance, file may not exist yet.
417 # https://bugs.launchpad.net/charm-helpers/+bug/1474030
418 self.log.debug('Attempt {} to get {} file mtime on {} '
419 'failed\n{}'.format(tries, filename,
420 unit_name, e))
421 time.sleep(retry_sleep_time)
422 tries += 1
423
424 if not file_mtime:
425 self.log.warn('Could not determine file mtime, assuming '
426 'file does not exist')
427 return False
428
402 if file_mtime >= mtime:429 if file_mtime >= mtime:
403 self.log.debug('File mtime is newer than provided mtime '430 self.log.debug('File mtime is newer than provided mtime '
404 '(%s >= %s)' % (file_mtime, mtime))431 '(%s >= %s) on %s (OK)' % (file_mtime,
432 mtime, unit_name))
405 return True433 return True
406 else:434 else:
407 self.log.warn('File mtime %s is older than provided mtime %s'435 self.log.warn('File mtime is older than provided mtime'
408 % (file_mtime, mtime))436 '(%s < on %s) on %s' % (file_mtime,
437 mtime, unit_name))
409 return False438 return False
410439
411 def validate_service_config_changed(self, sentry_unit, mtime, service,440 def validate_service_config_changed(self, sentry_unit, mtime, service,
412 filename, pgrep_full=None,441 filename, pgrep_full=None,
413 sleep_time=20, retry_count=2,442 sleep_time=20, retry_count=30,
414 retry_sleep_time=30):443 retry_sleep_time=10):
415 """Check service and file were updated after mtime444 """Check service and file were updated after mtime
416445
417 Args:446 Args:
@@ -456,7 +485,9 @@
456 sentry_unit,485 sentry_unit,
457 filename,486 filename,
458 mtime,487 mtime,
459 sleep_time=0)488 sleep_time=sleep_time,
489 retry_count=retry_count,
490 retry_sleep_time=retry_sleep_time)
460491
461 return service_restart and config_update492 return service_restart and config_update
462493
463494
=== modified file 'tests/charmhelpers/contrib/openstack/amulet/deployment.py'
--- tests/charmhelpers/contrib/openstack/amulet/deployment.py 2015-09-10 09:30:00 +0000
+++ tests/charmhelpers/contrib/openstack/amulet/deployment.py 2015-09-27 18:46:57 +0000
@@ -58,19 +58,17 @@
58 else:58 else:
59 base_series = self.current_next59 base_series = self.current_next
6060
61 if self.stable:61 for svc in other_services:
62 for svc in other_services:62 if svc['name'] in force_series_current:
63 if svc['name'] in force_series_current:63 base_series = self.current_next
64 base_series = self.current_next64 # If a location has been explicitly set, use it
6565 if svc.get('location'):
66 continue
67 if self.stable:
66 temp = 'lp:charms/{}/{}'68 temp = 'lp:charms/{}/{}'
67 svc['location'] = temp.format(base_series,69 svc['location'] = temp.format(base_series,
68 svc['name'])70 svc['name'])
69 else:71 else:
70 for svc in other_services:
71 if svc['name'] in force_series_current:
72 base_series = self.current_next
73
74 if svc['name'] in base_charms:72 if svc['name'] in base_charms:
75 temp = 'lp:charms/{}/{}'73 temp = 'lp:charms/{}/{}'
76 svc['location'] = temp.format(base_series,74 svc['location'] = temp.format(base_series,
@@ -79,6 +77,7 @@
79 temp = 'lp:~openstack-charmers/charms/{}/{}/next'77 temp = 'lp:~openstack-charmers/charms/{}/{}/next'
80 svc['location'] = temp.format(self.current_next,78 svc['location'] = temp.format(self.current_next,
81 svc['name'])79 svc['name'])
80
82 return other_services81 return other_services
8382
84 def _add_services(self, this_service, other_services):83 def _add_services(self, this_service, other_services):

Subscribers

People subscribed via source and target branches