Merge lp:~1chb1n/charms/trusty/mongodb/ch-sync-mitaka into lp:charms/trusty/mongodb
- Trusty Tahr (14.04)
- ch-sync-mitaka
- Merge into trunk
Proposed by
Ryan Beisner
on 2016-01-11
| Status: | Merged |
|---|---|
| Merged at revision: | 82 |
| Proposed branch: | lp:~1chb1n/charms/trusty/mongodb/ch-sync-mitaka |
| Merge into: | lp:charms/trusty/mongodb |
| Diff against target: |
839 lines (+302/-118) 11 files modified
charmhelpers/contrib/charmsupport/nrpe.py (+52/-14) charmhelpers/contrib/python/packages.py (+13/-4) charmhelpers/core/hookenv.py (+54/-6) charmhelpers/core/host.py (+94/-22) charmhelpers/core/hugepage.py (+2/-0) charmhelpers/core/services/helpers.py (+14/-5) charmhelpers/core/templating.py (+21/-8) charmhelpers/fetch/__init__.py (+10/-2) charmhelpers/fetch/archiveurl.py (+1/-1) charmhelpers/fetch/bzrurl.py (+22/-32) charmhelpers/fetch/giturl.py (+19/-24) |
| To merge this branch: | bzr merge lp:~1chb1n/charms/trusty/mongodb/ch-sync-mitaka |
| Related bugs: |
| Reviewer | Review Type | Date Requested | Status |
|---|---|---|---|
| Review Queue (community) | automated testing | Approve on 2016-01-15 | |
| José Antonio Rey | 2016-01-11 | Approve on 2016-01-13 | |
|
Review via email:
|
|||
Commit Message
Description of the Change
Sync charm-helpers for Mitaka cloud archive capability.
To post a comment you must log in.
charm_unit_test #15963 mongodb for 1chb1n mp282211
UNIT OK: passed
charm_amulet_test #8698 mongodb for 1chb1n mp282211
AMULET OK: passed
Build: http://
| Review Queue (review-queue) wrote : | # |
This item has failed automated testing! Results available here http://
review:
Needs Fixing
(automated testing)
| Review Queue (review-queue) wrote : | # |
The results (PASS) are in and available here: http://
review:
Approve
(automated testing)
Preview Diff
[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
| 1 | === modified file 'charmhelpers/contrib/charmsupport/nrpe.py' |
| 2 | --- charmhelpers/contrib/charmsupport/nrpe.py 2015-08-19 13:58:52 +0000 |
| 3 | +++ charmhelpers/contrib/charmsupport/nrpe.py 2016-01-11 18:37:30 +0000 |
| 4 | @@ -148,6 +148,13 @@ |
| 5 | self.description = description |
| 6 | self.check_cmd = self._locate_cmd(check_cmd) |
| 7 | |
| 8 | + def _get_check_filename(self): |
| 9 | + return os.path.join(NRPE.nrpe_confdir, '{}.cfg'.format(self.command)) |
| 10 | + |
| 11 | + def _get_service_filename(self, hostname): |
| 12 | + return os.path.join(NRPE.nagios_exportdir, |
| 13 | + 'service__{}_{}.cfg'.format(hostname, self.command)) |
| 14 | + |
| 15 | def _locate_cmd(self, check_cmd): |
| 16 | search_path = ( |
| 17 | '/usr/lib/nagios/plugins', |
| 18 | @@ -163,9 +170,21 @@ |
| 19 | log('Check command not found: {}'.format(parts[0])) |
| 20 | return '' |
| 21 | |
| 22 | + def _remove_service_files(self): |
| 23 | + if not os.path.exists(NRPE.nagios_exportdir): |
| 24 | + return |
| 25 | + for f in os.listdir(NRPE.nagios_exportdir): |
| 26 | + if f.endswith('_{}.cfg'.format(self.command)): |
| 27 | + os.remove(os.path.join(NRPE.nagios_exportdir, f)) |
| 28 | + |
| 29 | + def remove(self, hostname): |
| 30 | + nrpe_check_file = self._get_check_filename() |
| 31 | + if os.path.exists(nrpe_check_file): |
| 32 | + os.remove(nrpe_check_file) |
| 33 | + self._remove_service_files() |
| 34 | + |
| 35 | def write(self, nagios_context, hostname, nagios_servicegroups): |
| 36 | - nrpe_check_file = '/etc/nagios/nrpe.d/{}.cfg'.format( |
| 37 | - self.command) |
| 38 | + nrpe_check_file = self._get_check_filename() |
| 39 | with open(nrpe_check_file, 'w') as nrpe_check_config: |
| 40 | nrpe_check_config.write("# check {}\n".format(self.shortname)) |
| 41 | nrpe_check_config.write("command[{}]={}\n".format( |
| 42 | @@ -180,9 +199,7 @@ |
| 43 | |
| 44 | def write_service_config(self, nagios_context, hostname, |
| 45 | nagios_servicegroups): |
| 46 | - for f in os.listdir(NRPE.nagios_exportdir): |
| 47 | - if re.search('.*{}.cfg'.format(self.command), f): |
| 48 | - os.remove(os.path.join(NRPE.nagios_exportdir, f)) |
| 49 | + self._remove_service_files() |
| 50 | |
| 51 | templ_vars = { |
| 52 | 'nagios_hostname': hostname, |
| 53 | @@ -192,8 +209,7 @@ |
| 54 | 'command': self.command, |
| 55 | } |
| 56 | nrpe_service_text = Check.service_template.format(**templ_vars) |
| 57 | - nrpe_service_file = '{}/service__{}_{}.cfg'.format( |
| 58 | - NRPE.nagios_exportdir, hostname, self.command) |
| 59 | + nrpe_service_file = self._get_service_filename(hostname) |
| 60 | with open(nrpe_service_file, 'w') as nrpe_service_config: |
| 61 | nrpe_service_config.write(str(nrpe_service_text)) |
| 62 | |
| 63 | @@ -218,12 +234,32 @@ |
| 64 | if hostname: |
| 65 | self.hostname = hostname |
| 66 | else: |
| 67 | - self.hostname = "{}-{}".format(self.nagios_context, self.unit_name) |
| 68 | + nagios_hostname = get_nagios_hostname() |
| 69 | + if nagios_hostname: |
| 70 | + self.hostname = nagios_hostname |
| 71 | + else: |
| 72 | + self.hostname = "{}-{}".format(self.nagios_context, self.unit_name) |
| 73 | self.checks = [] |
| 74 | |
| 75 | def add_check(self, *args, **kwargs): |
| 76 | self.checks.append(Check(*args, **kwargs)) |
| 77 | |
| 78 | + def remove_check(self, *args, **kwargs): |
| 79 | + if kwargs.get('shortname') is None: |
| 80 | + raise ValueError('shortname of check must be specified') |
| 81 | + |
| 82 | + # Use sensible defaults if they're not specified - these are not |
| 83 | + # actually used during removal, but they're required for constructing |
| 84 | + # the Check object; check_disk is chosen because it's part of the |
| 85 | + # nagios-plugins-basic package. |
| 86 | + if kwargs.get('check_cmd') is None: |
| 87 | + kwargs['check_cmd'] = 'check_disk' |
| 88 | + if kwargs.get('description') is None: |
| 89 | + kwargs['description'] = '' |
| 90 | + |
| 91 | + check = Check(*args, **kwargs) |
| 92 | + check.remove(self.hostname) |
| 93 | + |
| 94 | def write(self): |
| 95 | try: |
| 96 | nagios_uid = pwd.getpwnam('nagios').pw_uid |
| 97 | @@ -260,7 +296,7 @@ |
| 98 | :param str relation_name: Name of relation nrpe sub joined to |
| 99 | """ |
| 100 | for rel in relations_of_type(relation_name): |
| 101 | - if 'nagios_hostname' in rel: |
| 102 | + if 'nagios_host_context' in rel: |
| 103 | return rel['nagios_host_context'] |
| 104 | |
| 105 | |
| 106 | @@ -301,11 +337,13 @@ |
| 107 | upstart_init = '/etc/init/%s.conf' % svc |
| 108 | sysv_init = '/etc/init.d/%s' % svc |
| 109 | if os.path.exists(upstart_init): |
| 110 | - nrpe.add_check( |
| 111 | - shortname=svc, |
| 112 | - description='process check {%s}' % unit_name, |
| 113 | - check_cmd='check_upstart_job %s' % svc |
| 114 | - ) |
| 115 | + # Don't add a check for these services from neutron-gateway |
| 116 | + if svc not in ['ext-port', 'os-charm-phy-nic-mtu']: |
| 117 | + nrpe.add_check( |
| 118 | + shortname=svc, |
| 119 | + description='process check {%s}' % unit_name, |
| 120 | + check_cmd='check_upstart_job %s' % svc |
| 121 | + ) |
| 122 | elif os.path.exists(sysv_init): |
| 123 | cronpath = '/etc/cron.d/nagios-service-check-%s' % svc |
| 124 | cron_file = ('*/5 * * * * root ' |
| 125 | |
| 126 | === modified file 'charmhelpers/contrib/python/packages.py' |
| 127 | --- charmhelpers/contrib/python/packages.py 2015-08-19 13:58:52 +0000 |
| 128 | +++ charmhelpers/contrib/python/packages.py 2016-01-11 18:37:30 +0000 |
| 129 | @@ -42,8 +42,12 @@ |
| 130 | yield "--{0}={1}".format(key, value) |
| 131 | |
| 132 | |
| 133 | -def pip_install_requirements(requirements, **options): |
| 134 | - """Install a requirements file """ |
| 135 | +def pip_install_requirements(requirements, constraints=None, **options): |
| 136 | + """Install a requirements file. |
| 137 | + |
| 138 | + :param constraints: Path to pip constraints file. |
| 139 | + http://pip.readthedocs.org/en/stable/user_guide/#constraints-files |
| 140 | + """ |
| 141 | command = ["install"] |
| 142 | |
| 143 | available_options = ('proxy', 'src', 'log', ) |
| 144 | @@ -51,8 +55,13 @@ |
| 145 | command.append(option) |
| 146 | |
| 147 | command.append("-r {0}".format(requirements)) |
| 148 | - log("Installing from file: {} with options: {}".format(requirements, |
| 149 | - command)) |
| 150 | + if constraints: |
| 151 | + command.append("-c {0}".format(constraints)) |
| 152 | + log("Installing from file: {} with constraints {} " |
| 153 | + "and options: {}".format(requirements, constraints, command)) |
| 154 | + else: |
| 155 | + log("Installing from file: {} with options: {}".format(requirements, |
| 156 | + command)) |
| 157 | pip_execute(command) |
| 158 | |
| 159 | |
| 160 | |
| 161 | === modified file 'charmhelpers/core/hookenv.py' |
| 162 | --- charmhelpers/core/hookenv.py 2015-10-07 10:45:38 +0000 |
| 163 | +++ charmhelpers/core/hookenv.py 2016-01-11 18:37:30 +0000 |
| 164 | @@ -491,6 +491,19 @@ |
| 165 | |
| 166 | |
| 167 | @cached |
| 168 | +def peer_relation_id(): |
| 169 | + '''Get the peers relation id if a peers relation has been joined, else None.''' |
| 170 | + md = metadata() |
| 171 | + section = md.get('peers') |
| 172 | + if section: |
| 173 | + for key in section: |
| 174 | + relids = relation_ids(key) |
| 175 | + if relids: |
| 176 | + return relids[0] |
| 177 | + return None |
| 178 | + |
| 179 | + |
| 180 | +@cached |
| 181 | def relation_to_interface(relation_name): |
| 182 | """ |
| 183 | Given the name of a relation, return the interface that relation uses. |
| 184 | @@ -504,12 +517,12 @@ |
| 185 | def relation_to_role_and_interface(relation_name): |
| 186 | """ |
| 187 | Given the name of a relation, return the role and the name of the interface |
| 188 | - that relation uses (where role is one of ``provides``, ``requires``, or ``peer``). |
| 189 | + that relation uses (where role is one of ``provides``, ``requires``, or ``peers``). |
| 190 | |
| 191 | :returns: A tuple containing ``(role, interface)``, or ``(None, None)``. |
| 192 | """ |
| 193 | _metadata = metadata() |
| 194 | - for role in ('provides', 'requires', 'peer'): |
| 195 | + for role in ('provides', 'requires', 'peers'): |
| 196 | interface = _metadata.get(role, {}).get(relation_name, {}).get('interface') |
| 197 | if interface: |
| 198 | return role, interface |
| 199 | @@ -521,7 +534,7 @@ |
| 200 | """ |
| 201 | Given a role and interface name, return a list of relation names for the |
| 202 | current charm that use that interface under that role (where role is one |
| 203 | - of ``provides``, ``requires``, or ``peer``). |
| 204 | + of ``provides``, ``requires``, or ``peers``). |
| 205 | |
| 206 | :returns: A list of relation names. |
| 207 | """ |
| 208 | @@ -542,7 +555,7 @@ |
| 209 | :returns: A list of relation names. |
| 210 | """ |
| 211 | results = [] |
| 212 | - for role in ('provides', 'requires', 'peer'): |
| 213 | + for role in ('provides', 'requires', 'peers'): |
| 214 | results.extend(role_and_interface_to_relations(role, interface_name)) |
| 215 | return results |
| 216 | |
| 217 | @@ -624,7 +637,7 @@ |
| 218 | |
| 219 | |
| 220 | @cached |
| 221 | -def storage_get(attribute="", storage_id=""): |
| 222 | +def storage_get(attribute=None, storage_id=None): |
| 223 | """Get storage attributes""" |
| 224 | _args = ['storage-get', '--format=json'] |
| 225 | if storage_id: |
| 226 | @@ -638,7 +651,7 @@ |
| 227 | |
| 228 | |
| 229 | @cached |
| 230 | -def storage_list(storage_name=""): |
| 231 | +def storage_list(storage_name=None): |
| 232 | """List the storage IDs for the unit""" |
| 233 | _args = ['storage-list', '--format=json'] |
| 234 | if storage_name: |
| 235 | @@ -820,6 +833,7 @@ |
| 236 | |
| 237 | def translate_exc(from_exc, to_exc): |
| 238 | def inner_translate_exc1(f): |
| 239 | + @wraps(f) |
| 240 | def inner_translate_exc2(*args, **kwargs): |
| 241 | try: |
| 242 | return f(*args, **kwargs) |
| 243 | @@ -864,6 +878,40 @@ |
| 244 | subprocess.check_call(cmd) |
| 245 | |
| 246 | |
| 247 | +@translate_exc(from_exc=OSError, to_exc=NotImplementedError) |
| 248 | +def payload_register(ptype, klass, pid): |
| 249 | + """ is used while a hook is running to let Juju know that a |
| 250 | + payload has been started.""" |
| 251 | + cmd = ['payload-register'] |
| 252 | + for x in [ptype, klass, pid]: |
| 253 | + cmd.append(x) |
| 254 | + subprocess.check_call(cmd) |
| 255 | + |
| 256 | + |
| 257 | +@translate_exc(from_exc=OSError, to_exc=NotImplementedError) |
| 258 | +def payload_unregister(klass, pid): |
| 259 | + """ is used while a hook is running to let Juju know |
| 260 | + that a payload has been manually stopped. The <class> and <id> provided |
| 261 | + must match a payload that has been previously registered with juju using |
| 262 | + payload-register.""" |
| 263 | + cmd = ['payload-unregister'] |
| 264 | + for x in [klass, pid]: |
| 265 | + cmd.append(x) |
| 266 | + subprocess.check_call(cmd) |
| 267 | + |
| 268 | + |
| 269 | +@translate_exc(from_exc=OSError, to_exc=NotImplementedError) |
| 270 | +def payload_status_set(klass, pid, status): |
| 271 | + """is used to update the current status of a registered payload. |
| 272 | + The <class> and <id> provided must match a payload that has been previously |
| 273 | + registered with juju using payload-register. The <status> must be one of the |
| 274 | + follow: starting, started, stopping, stopped""" |
| 275 | + cmd = ['payload-status-set'] |
| 276 | + for x in [klass, pid, status]: |
| 277 | + cmd.append(x) |
| 278 | + subprocess.check_call(cmd) |
| 279 | + |
| 280 | + |
| 281 | @cached |
| 282 | def juju_version(): |
| 283 | """Full version string (eg. '1.23.3.1-trusty-amd64')""" |
| 284 | |
| 285 | === modified file 'charmhelpers/core/host.py' |
| 286 | --- charmhelpers/core/host.py 2015-10-07 10:45:38 +0000 |
| 287 | +++ charmhelpers/core/host.py 2016-01-11 18:37:30 +0000 |
| 288 | @@ -67,10 +67,14 @@ |
| 289 | """Pause a system service. |
| 290 | |
| 291 | Stop it, and prevent it from starting again at boot.""" |
| 292 | - stopped = service_stop(service_name) |
| 293 | + stopped = True |
| 294 | + if service_running(service_name): |
| 295 | + stopped = service_stop(service_name) |
| 296 | upstart_file = os.path.join(init_dir, "{}.conf".format(service_name)) |
| 297 | sysv_file = os.path.join(initd_dir, service_name) |
| 298 | - if os.path.exists(upstart_file): |
| 299 | + if init_is_systemd(): |
| 300 | + service('disable', service_name) |
| 301 | + elif os.path.exists(upstart_file): |
| 302 | override_path = os.path.join( |
| 303 | init_dir, '{}.override'.format(service_name)) |
| 304 | with open(override_path, 'w') as fh: |
| 305 | @@ -78,9 +82,9 @@ |
| 306 | elif os.path.exists(sysv_file): |
| 307 | subprocess.check_call(["update-rc.d", service_name, "disable"]) |
| 308 | else: |
| 309 | - # XXX: Support SystemD too |
| 310 | raise ValueError( |
| 311 | - "Unable to detect {0} as either Upstart {1} or SysV {2}".format( |
| 312 | + "Unable to detect {0} as SystemD, Upstart {1} or" |
| 313 | + " SysV {2}".format( |
| 314 | service_name, upstart_file, sysv_file)) |
| 315 | return stopped |
| 316 | |
| 317 | @@ -92,7 +96,9 @@ |
| 318 | Reenable starting again at boot. Start the service""" |
| 319 | upstart_file = os.path.join(init_dir, "{}.conf".format(service_name)) |
| 320 | sysv_file = os.path.join(initd_dir, service_name) |
| 321 | - if os.path.exists(upstart_file): |
| 322 | + if init_is_systemd(): |
| 323 | + service('enable', service_name) |
| 324 | + elif os.path.exists(upstart_file): |
| 325 | override_path = os.path.join( |
| 326 | init_dir, '{}.override'.format(service_name)) |
| 327 | if os.path.exists(override_path): |
| 328 | @@ -100,34 +106,42 @@ |
| 329 | elif os.path.exists(sysv_file): |
| 330 | subprocess.check_call(["update-rc.d", service_name, "enable"]) |
| 331 | else: |
| 332 | - # XXX: Support SystemD too |
| 333 | raise ValueError( |
| 334 | - "Unable to detect {0} as either Upstart {1} or SysV {2}".format( |
| 335 | + "Unable to detect {0} as SystemD, Upstart {1} or" |
| 336 | + " SysV {2}".format( |
| 337 | service_name, upstart_file, sysv_file)) |
| 338 | |
| 339 | - started = service_start(service_name) |
| 340 | + started = service_running(service_name) |
| 341 | + if not started: |
| 342 | + started = service_start(service_name) |
| 343 | return started |
| 344 | |
| 345 | |
| 346 | def service(action, service_name): |
| 347 | """Control a system service""" |
| 348 | - cmd = ['service', service_name, action] |
| 349 | + if init_is_systemd(): |
| 350 | + cmd = ['systemctl', action, service_name] |
| 351 | + else: |
| 352 | + cmd = ['service', service_name, action] |
| 353 | return subprocess.call(cmd) == 0 |
| 354 | |
| 355 | |
| 356 | -def service_running(service): |
| 357 | +def service_running(service_name): |
| 358 | """Determine whether a system service is running""" |
| 359 | - try: |
| 360 | - output = subprocess.check_output( |
| 361 | - ['service', service, 'status'], |
| 362 | - stderr=subprocess.STDOUT).decode('UTF-8') |
| 363 | - except subprocess.CalledProcessError: |
| 364 | - return False |
| 365 | + if init_is_systemd(): |
| 366 | + return service('is-active', service_name) |
| 367 | else: |
| 368 | - if ("start/running" in output or "is running" in output): |
| 369 | - return True |
| 370 | - else: |
| 371 | + try: |
| 372 | + output = subprocess.check_output( |
| 373 | + ['service', service_name, 'status'], |
| 374 | + stderr=subprocess.STDOUT).decode('UTF-8') |
| 375 | + except subprocess.CalledProcessError: |
| 376 | return False |
| 377 | + else: |
| 378 | + if ("start/running" in output or "is running" in output): |
| 379 | + return True |
| 380 | + else: |
| 381 | + return False |
| 382 | |
| 383 | |
| 384 | def service_available(service_name): |
| 385 | @@ -142,8 +156,29 @@ |
| 386 | return True |
| 387 | |
| 388 | |
| 389 | -def adduser(username, password=None, shell='/bin/bash', system_user=False): |
| 390 | - """Add a user to the system""" |
| 391 | +SYSTEMD_SYSTEM = '/run/systemd/system' |
| 392 | + |
| 393 | + |
| 394 | +def init_is_systemd(): |
| 395 | + return os.path.isdir(SYSTEMD_SYSTEM) |
| 396 | + |
| 397 | + |
| 398 | +def adduser(username, password=None, shell='/bin/bash', system_user=False, |
| 399 | + primary_group=None, secondary_groups=None): |
| 400 | + """ |
| 401 | + Add a user to the system. |
| 402 | + |
| 403 | + Will log but otherwise succeed if the user already exists. |
| 404 | + |
| 405 | + :param str username: Username to create |
| 406 | + :param str password: Password for user; if ``None``, create a system user |
| 407 | + :param str shell: The default shell for the user |
| 408 | + :param bool system_user: Whether to create a login or system user |
| 409 | + :param str primary_group: Primary group for user; defaults to their username |
| 410 | + :param list secondary_groups: Optional list of additional groups |
| 411 | + |
| 412 | + :returns: The password database entry struct, as returned by `pwd.getpwnam` |
| 413 | + """ |
| 414 | try: |
| 415 | user_info = pwd.getpwnam(username) |
| 416 | log('user {0} already exists!'.format(username)) |
| 417 | @@ -158,6 +193,16 @@ |
| 418 | '--shell', shell, |
| 419 | '--password', password, |
| 420 | ]) |
| 421 | + if not primary_group: |
| 422 | + try: |
| 423 | + grp.getgrnam(username) |
| 424 | + primary_group = username # avoid "group exists" error |
| 425 | + except KeyError: |
| 426 | + pass |
| 427 | + if primary_group: |
| 428 | + cmd.extend(['-g', primary_group]) |
| 429 | + if secondary_groups: |
| 430 | + cmd.extend(['-G', ','.join(secondary_groups)]) |
| 431 | cmd.append(username) |
| 432 | subprocess.check_call(cmd) |
| 433 | user_info = pwd.getpwnam(username) |
| 434 | @@ -566,7 +611,14 @@ |
| 435 | os.chdir(cur) |
| 436 | |
| 437 | |
| 438 | -def chownr(path, owner, group, follow_links=True): |
| 439 | +def chownr(path, owner, group, follow_links=True, chowntopdir=False): |
| 440 | + """ |
| 441 | + Recursively change user and group ownership of files and directories |
| 442 | + in given path. Doesn't chown path itself by default, only its children. |
| 443 | + |
| 444 | + :param bool follow_links: Also Chown links if True |
| 445 | + :param bool chowntopdir: Also chown path itself if True |
| 446 | + """ |
| 447 | uid = pwd.getpwnam(owner).pw_uid |
| 448 | gid = grp.getgrnam(group).gr_gid |
| 449 | if follow_links: |
| 450 | @@ -574,6 +626,10 @@ |
| 451 | else: |
| 452 | chown = os.lchown |
| 453 | |
| 454 | + if chowntopdir: |
| 455 | + broken_symlink = os.path.lexists(path) and not os.path.exists(path) |
| 456 | + if not broken_symlink: |
| 457 | + chown(path, uid, gid) |
| 458 | for root, dirs, files in os.walk(path): |
| 459 | for name in dirs + files: |
| 460 | full = os.path.join(root, name) |
| 461 | @@ -584,3 +640,19 @@ |
| 462 | |
| 463 | def lchownr(path, owner, group): |
| 464 | chownr(path, owner, group, follow_links=False) |
| 465 | + |
| 466 | + |
| 467 | +def get_total_ram(): |
| 468 | + '''The total amount of system RAM in bytes. |
| 469 | + |
| 470 | + This is what is reported by the OS, and may be overcommitted when |
| 471 | + there are multiple containers hosted on the same machine. |
| 472 | + ''' |
| 473 | + with open('/proc/meminfo', 'r') as f: |
| 474 | + for line in f.readlines(): |
| 475 | + if line: |
| 476 | + key, value, unit = line.split() |
| 477 | + if key == 'MemTotal:': |
| 478 | + assert unit == 'kB', 'Unknown unit' |
| 479 | + return int(value) * 1024 # Classic, not KiB. |
| 480 | + raise NotImplementedError() |
| 481 | |
| 482 | === modified file 'charmhelpers/core/hugepage.py' |
| 483 | --- charmhelpers/core/hugepage.py 2015-10-07 10:45:38 +0000 |
| 484 | +++ charmhelpers/core/hugepage.py 2016-01-11 18:37:30 +0000 |
| 485 | @@ -46,6 +46,8 @@ |
| 486 | group_info = add_group(group) |
| 487 | gid = group_info.gr_gid |
| 488 | add_user_to_group(user, group) |
| 489 | + if max_map_count < 2 * nr_hugepages: |
| 490 | + max_map_count = 2 * nr_hugepages |
| 491 | sysctl_settings = { |
| 492 | 'vm.nr_hugepages': nr_hugepages, |
| 493 | 'vm.max_map_count': max_map_count, |
| 494 | |
| 495 | === modified file 'charmhelpers/core/services/helpers.py' |
| 496 | --- charmhelpers/core/services/helpers.py 2015-08-19 13:58:52 +0000 |
| 497 | +++ charmhelpers/core/services/helpers.py 2016-01-11 18:37:30 +0000 |
| 498 | @@ -243,33 +243,40 @@ |
| 499 | :param str source: The template source file, relative to |
| 500 | `$CHARM_DIR/templates` |
| 501 | |
| 502 | - :param str target: The target to write the rendered template to |
| 503 | + :param str target: The target to write the rendered template to (or None) |
| 504 | :param str owner: The owner of the rendered file |
| 505 | :param str group: The group of the rendered file |
| 506 | :param int perms: The permissions of the rendered file |
| 507 | :param partial on_change_action: functools partial to be executed when |
| 508 | rendered file changes |
| 509 | + :param jinja2 loader template_loader: A jinja2 template loader |
| 510 | + |
| 511 | + :return str: The rendered template |
| 512 | """ |
| 513 | def __init__(self, source, target, |
| 514 | owner='root', group='root', perms=0o444, |
| 515 | - on_change_action=None): |
| 516 | + on_change_action=None, template_loader=None): |
| 517 | self.source = source |
| 518 | self.target = target |
| 519 | self.owner = owner |
| 520 | self.group = group |
| 521 | self.perms = perms |
| 522 | self.on_change_action = on_change_action |
| 523 | + self.template_loader = template_loader |
| 524 | |
| 525 | def __call__(self, manager, service_name, event_name): |
| 526 | pre_checksum = '' |
| 527 | if self.on_change_action and os.path.isfile(self.target): |
| 528 | pre_checksum = host.file_hash(self.target) |
| 529 | service = manager.get_service(service_name) |
| 530 | - context = {} |
| 531 | + context = {'ctx': {}} |
| 532 | for ctx in service.get('required_data', []): |
| 533 | context.update(ctx) |
| 534 | - templating.render(self.source, self.target, context, |
| 535 | - self.owner, self.group, self.perms) |
| 536 | + context['ctx'].update(ctx) |
| 537 | + |
| 538 | + result = templating.render(self.source, self.target, context, |
| 539 | + self.owner, self.group, self.perms, |
| 540 | + template_loader=self.template_loader) |
| 541 | if self.on_change_action: |
| 542 | if pre_checksum == host.file_hash(self.target): |
| 543 | hookenv.log( |
| 544 | @@ -278,6 +285,8 @@ |
| 545 | else: |
| 546 | self.on_change_action() |
| 547 | |
| 548 | + return result |
| 549 | + |
| 550 | |
| 551 | # Convenience aliases for templates |
| 552 | render_template = template = TemplateCallback |
| 553 | |
| 554 | === modified file 'charmhelpers/core/templating.py' |
| 555 | --- charmhelpers/core/templating.py 2015-02-25 00:24:56 +0000 |
| 556 | +++ charmhelpers/core/templating.py 2016-01-11 18:37:30 +0000 |
| 557 | @@ -21,13 +21,14 @@ |
| 558 | |
| 559 | |
| 560 | def render(source, target, context, owner='root', group='root', |
| 561 | - perms=0o444, templates_dir=None, encoding='UTF-8'): |
| 562 | + perms=0o444, templates_dir=None, encoding='UTF-8', template_loader=None): |
| 563 | """ |
| 564 | Render a template. |
| 565 | |
| 566 | The `source` path, if not absolute, is relative to the `templates_dir`. |
| 567 | |
| 568 | - The `target` path should be absolute. |
| 569 | + The `target` path should be absolute. It can also be `None`, in which |
| 570 | + case no file will be written. |
| 571 | |
| 572 | The context should be a dict containing the values to be replaced in the |
| 573 | template. |
| 574 | @@ -36,6 +37,9 @@ |
| 575 | |
| 576 | If omitted, `templates_dir` defaults to the `templates` folder in the charm. |
| 577 | |
| 578 | + The rendered template will be written to the file as well as being returned |
| 579 | + as a string. |
| 580 | + |
| 581 | Note: Using this requires python-jinja2; if it is not installed, calling |
| 582 | this will attempt to use charmhelpers.fetch.apt_install to install it. |
| 583 | """ |
| 584 | @@ -52,17 +56,26 @@ |
| 585 | apt_install('python-jinja2', fatal=True) |
| 586 | from jinja2 import FileSystemLoader, Environment, exceptions |
| 587 | |
| 588 | - if templates_dir is None: |
| 589 | - templates_dir = os.path.join(hookenv.charm_dir(), 'templates') |
| 590 | - loader = Environment(loader=FileSystemLoader(templates_dir)) |
| 591 | + if template_loader: |
| 592 | + template_env = Environment(loader=template_loader) |
| 593 | + else: |
| 594 | + if templates_dir is None: |
| 595 | + templates_dir = os.path.join(hookenv.charm_dir(), 'templates') |
| 596 | + template_env = Environment(loader=FileSystemLoader(templates_dir)) |
| 597 | try: |
| 598 | source = source |
| 599 | - template = loader.get_template(source) |
| 600 | + template = template_env.get_template(source) |
| 601 | except exceptions.TemplateNotFound as e: |
| 602 | hookenv.log('Could not load template %s from %s.' % |
| 603 | (source, templates_dir), |
| 604 | level=hookenv.ERROR) |
| 605 | raise e |
| 606 | content = template.render(context) |
| 607 | - host.mkdir(os.path.dirname(target), owner, group, perms=0o755) |
| 608 | - host.write_file(target, content.encode(encoding), owner, group, perms) |
| 609 | + if target is not None: |
| 610 | + target_dir = os.path.dirname(target) |
| 611 | + if not os.path.exists(target_dir): |
| 612 | + # This is a terrible default directory permission, as the file |
| 613 | + # or its siblings will often contain secrets. |
| 614 | + host.mkdir(os.path.dirname(target), owner, group, perms=0o755) |
| 615 | + host.write_file(target, content.encode(encoding), owner, group, perms) |
| 616 | + return content |
| 617 | |
| 618 | === modified file 'charmhelpers/fetch/__init__.py' |
| 619 | --- charmhelpers/fetch/__init__.py 2015-08-19 00:54:50 +0000 |
| 620 | +++ charmhelpers/fetch/__init__.py 2016-01-11 18:37:30 +0000 |
| 621 | @@ -98,6 +98,14 @@ |
| 622 | 'liberty/proposed': 'trusty-proposed/liberty', |
| 623 | 'trusty-liberty/proposed': 'trusty-proposed/liberty', |
| 624 | 'trusty-proposed/liberty': 'trusty-proposed/liberty', |
| 625 | + # Mitaka |
| 626 | + 'mitaka': 'trusty-updates/mitaka', |
| 627 | + 'trusty-mitaka': 'trusty-updates/mitaka', |
| 628 | + 'trusty-mitaka/updates': 'trusty-updates/mitaka', |
| 629 | + 'trusty-updates/mitaka': 'trusty-updates/mitaka', |
| 630 | + 'mitaka/proposed': 'trusty-proposed/mitaka', |
| 631 | + 'trusty-mitaka/proposed': 'trusty-proposed/mitaka', |
| 632 | + 'trusty-proposed/mitaka': 'trusty-proposed/mitaka', |
| 633 | } |
| 634 | |
| 635 | # The order of this list is very important. Handlers should be listed in from |
| 636 | @@ -225,12 +233,12 @@ |
| 637 | |
| 638 | def apt_mark(packages, mark, fatal=False): |
| 639 | """Flag one or more packages using apt-mark""" |
| 640 | + log("Marking {} as {}".format(packages, mark)) |
| 641 | cmd = ['apt-mark', mark] |
| 642 | if isinstance(packages, six.string_types): |
| 643 | cmd.append(packages) |
| 644 | else: |
| 645 | cmd.extend(packages) |
| 646 | - log("Holding {}".format(packages)) |
| 647 | |
| 648 | if fatal: |
| 649 | subprocess.check_call(cmd, universal_newlines=True) |
| 650 | @@ -411,7 +419,7 @@ |
| 651 | importlib.import_module(package), |
| 652 | classname) |
| 653 | plugin_list.append(handler_class()) |
| 654 | - except (ImportError, AttributeError): |
| 655 | + except NotImplementedError: |
| 656 | # Skip missing plugins so that they can be ommitted from |
| 657 | # installation if desired |
| 658 | log("FetchHandler {} not found, skipping plugin".format( |
| 659 | |
| 660 | === modified file 'charmhelpers/fetch/archiveurl.py' |
| 661 | --- charmhelpers/fetch/archiveurl.py 2015-08-19 00:54:50 +0000 |
| 662 | +++ charmhelpers/fetch/archiveurl.py 2016-01-11 18:37:30 +0000 |
| 663 | @@ -108,7 +108,7 @@ |
| 664 | install_opener(opener) |
| 665 | response = urlopen(source) |
| 666 | try: |
| 667 | - with open(dest, 'w') as dest_file: |
| 668 | + with open(dest, 'wb') as dest_file: |
| 669 | dest_file.write(response.read()) |
| 670 | except Exception as e: |
| 671 | if os.path.isfile(dest): |
| 672 | |
| 673 | === modified file 'charmhelpers/fetch/bzrurl.py' |
| 674 | --- charmhelpers/fetch/bzrurl.py 2015-02-25 00:24:56 +0000 |
| 675 | +++ charmhelpers/fetch/bzrurl.py 2016-01-11 18:37:30 +0000 |
| 676 | @@ -15,60 +15,50 @@ |
| 677 | # along with charm-helpers. If not, see <http://www.gnu.org/licenses/>. |
| 678 | |
| 679 | import os |
| 680 | +from subprocess import check_call |
| 681 | from charmhelpers.fetch import ( |
| 682 | BaseFetchHandler, |
| 683 | - UnhandledSource |
| 684 | + UnhandledSource, |
| 685 | + filter_installed_packages, |
| 686 | + apt_install, |
| 687 | ) |
| 688 | from charmhelpers.core.host import mkdir |
| 689 | |
| 690 | -import six |
| 691 | -if six.PY3: |
| 692 | - raise ImportError('bzrlib does not support Python3') |
| 693 | |
| 694 | -try: |
| 695 | - from bzrlib.branch import Branch |
| 696 | - from bzrlib import bzrdir, workingtree, errors |
| 697 | -except ImportError: |
| 698 | - from charmhelpers.fetch import apt_install |
| 699 | - apt_install("python-bzrlib") |
| 700 | - from bzrlib.branch import Branch |
| 701 | - from bzrlib import bzrdir, workingtree, errors |
| 702 | +if filter_installed_packages(['bzr']) != []: |
| 703 | + apt_install(['bzr']) |
| 704 | + if filter_installed_packages(['bzr']) != []: |
| 705 | + raise NotImplementedError('Unable to install bzr') |
| 706 | |
| 707 | |
| 708 | class BzrUrlFetchHandler(BaseFetchHandler): |
| 709 | """Handler for bazaar branches via generic and lp URLs""" |
| 710 | def can_handle(self, source): |
| 711 | url_parts = self.parse_url(source) |
| 712 | - if url_parts.scheme not in ('bzr+ssh', 'lp'): |
| 713 | + if url_parts.scheme not in ('bzr+ssh', 'lp', ''): |
| 714 | return False |
| 715 | + elif not url_parts.scheme: |
| 716 | + return os.path.exists(os.path.join(source, '.bzr')) |
| 717 | else: |
| 718 | return True |
| 719 | |
| 720 | def branch(self, source, dest): |
| 721 | - url_parts = self.parse_url(source) |
| 722 | - # If we use lp:branchname scheme we need to load plugins |
| 723 | if not self.can_handle(source): |
| 724 | raise UnhandledSource("Cannot handle {}".format(source)) |
| 725 | - if url_parts.scheme == "lp": |
| 726 | - from bzrlib.plugin import load_plugins |
| 727 | - load_plugins() |
| 728 | - try: |
| 729 | - local_branch = bzrdir.BzrDir.create_branch_convenience(dest) |
| 730 | - except errors.AlreadyControlDirError: |
| 731 | - local_branch = Branch.open(dest) |
| 732 | - try: |
| 733 | - remote_branch = Branch.open(source) |
| 734 | - remote_branch.push(local_branch) |
| 735 | - tree = workingtree.WorkingTree.open(dest) |
| 736 | - tree.update() |
| 737 | - except Exception as e: |
| 738 | - raise e |
| 739 | + if os.path.exists(dest): |
| 740 | + check_call(['bzr', 'pull', '--overwrite', '-d', dest, source]) |
| 741 | + else: |
| 742 | + check_call(['bzr', 'branch', source, dest]) |
| 743 | |
| 744 | - def install(self, source): |
| 745 | + def install(self, source, dest=None): |
| 746 | url_parts = self.parse_url(source) |
| 747 | branch_name = url_parts.path.strip("/").split("/")[-1] |
| 748 | - dest_dir = os.path.join(os.environ.get('CHARM_DIR'), "fetched", |
| 749 | - branch_name) |
| 750 | + if dest: |
| 751 | + dest_dir = os.path.join(dest, branch_name) |
| 752 | + else: |
| 753 | + dest_dir = os.path.join(os.environ.get('CHARM_DIR'), "fetched", |
| 754 | + branch_name) |
| 755 | + |
| 756 | if not os.path.exists(dest_dir): |
| 757 | mkdir(dest_dir, perms=0o755) |
| 758 | try: |
| 759 | |
| 760 | === modified file 'charmhelpers/fetch/giturl.py' |
| 761 | --- charmhelpers/fetch/giturl.py 2015-08-19 00:54:50 +0000 |
| 762 | +++ charmhelpers/fetch/giturl.py 2016-01-11 18:37:30 +0000 |
| 763 | @@ -15,24 +15,18 @@ |
| 764 | # along with charm-helpers. If not, see <http://www.gnu.org/licenses/>. |
| 765 | |
| 766 | import os |
| 767 | +from subprocess import check_call |
| 768 | from charmhelpers.fetch import ( |
| 769 | BaseFetchHandler, |
| 770 | - UnhandledSource |
| 771 | + UnhandledSource, |
| 772 | + filter_installed_packages, |
| 773 | + apt_install, |
| 774 | ) |
| 775 | -from charmhelpers.core.host import mkdir |
| 776 | - |
| 777 | -import six |
| 778 | -if six.PY3: |
| 779 | - raise ImportError('GitPython does not support Python 3') |
| 780 | - |
| 781 | -try: |
| 782 | - from git import Repo |
| 783 | -except ImportError: |
| 784 | - from charmhelpers.fetch import apt_install |
| 785 | - apt_install("python-git") |
| 786 | - from git import Repo |
| 787 | - |
| 788 | -from git.exc import GitCommandError # noqa E402 |
| 789 | + |
| 790 | +if filter_installed_packages(['git']) != []: |
| 791 | + apt_install(['git']) |
| 792 | + if filter_installed_packages(['git']) != []: |
| 793 | + raise NotImplementedError('Unable to install git') |
| 794 | |
| 795 | |
| 796 | class GitUrlFetchHandler(BaseFetchHandler): |
| 797 | @@ -40,19 +34,24 @@ |
| 798 | def can_handle(self, source): |
| 799 | url_parts = self.parse_url(source) |
| 800 | # TODO (mattyw) no support for ssh git@ yet |
| 801 | - if url_parts.scheme not in ('http', 'https', 'git'): |
| 802 | + if url_parts.scheme not in ('http', 'https', 'git', ''): |
| 803 | return False |
| 804 | + elif not url_parts.scheme: |
| 805 | + return os.path.exists(os.path.join(source, '.git')) |
| 806 | else: |
| 807 | return True |
| 808 | |
| 809 | - def clone(self, source, dest, branch, depth=None): |
| 810 | + def clone(self, source, dest, branch="master", depth=None): |
| 811 | if not self.can_handle(source): |
| 812 | raise UnhandledSource("Cannot handle {}".format(source)) |
| 813 | |
| 814 | - if depth: |
| 815 | - Repo.clone_from(source, dest, branch=branch, depth=depth) |
| 816 | + if os.path.exists(dest): |
| 817 | + cmd = ['git', '-C', dest, 'pull', source, branch] |
| 818 | else: |
| 819 | - Repo.clone_from(source, dest, branch=branch) |
| 820 | + cmd = ['git', 'clone', source, dest, '--branch', branch] |
| 821 | + if depth: |
| 822 | + cmd.extend(['--depth', depth]) |
| 823 | + check_call(cmd) |
| 824 | |
| 825 | def install(self, source, branch="master", dest=None, depth=None): |
| 826 | url_parts = self.parse_url(source) |
| 827 | @@ -62,12 +61,8 @@ |
| 828 | else: |
| 829 | dest_dir = os.path.join(os.environ.get('CHARM_DIR'), "fetched", |
| 830 | branch_name) |
| 831 | - if not os.path.exists(dest_dir): |
| 832 | - mkdir(dest_dir, perms=0o755) |
| 833 | try: |
| 834 | self.clone(source, dest_dir, branch, depth) |
| 835 | - except GitCommandError as e: |
| 836 | - raise UnhandledSource(e) |
| 837 | except OSError as e: |
| 838 | raise UnhandledSource(e.strerror) |
| 839 | return dest_dir |

charm_lint_check #17089 mongodb for 1chb1n mp282211
LINT OK: passed
Build: http:// 10.245. 162.77: 8080/job/ charm_lint_ check/17089/