~1chb1n/charms/trusty/odl-controller/next-amulet-mitaka-1601

« back to all changes in this revision

Viewing changes to hooks/charmhelpers/core/host.py

  • Committer: Ryan Beisner
  • Date: 2016-01-22 04:17:02 UTC
  • Revision ID: ryan.beisner@canonical.com-20160122041702-uwi4yqwy92z4fc2k
sync charmhelpers for mitaka cloud archive recognition

Show diffs side-by-side

added added

removed removed

Lines of Context:
67
67
    """Pause a system service.
68
68
 
69
69
    Stop it, and prevent it from starting again at boot."""
70
 
    stopped = service_stop(service_name)
 
70
    stopped = True
 
71
    if service_running(service_name):
 
72
        stopped = service_stop(service_name)
71
73
    upstart_file = os.path.join(init_dir, "{}.conf".format(service_name))
72
74
    sysv_file = os.path.join(initd_dir, service_name)
73
 
    if os.path.exists(upstart_file):
 
75
    if init_is_systemd():
 
76
        service('disable', service_name)
 
77
    elif os.path.exists(upstart_file):
74
78
        override_path = os.path.join(
75
79
            init_dir, '{}.override'.format(service_name))
76
80
        with open(override_path, 'w') as fh:
78
82
    elif os.path.exists(sysv_file):
79
83
        subprocess.check_call(["update-rc.d", service_name, "disable"])
80
84
    else:
81
 
        # XXX: Support SystemD too
82
85
        raise ValueError(
83
 
            "Unable to detect {0} as either Upstart {1} or SysV {2}".format(
 
86
            "Unable to detect {0} as SystemD, Upstart {1} or"
 
87
            " SysV {2}".format(
84
88
                service_name, upstart_file, sysv_file))
85
89
    return stopped
86
90
 
92
96
    Reenable starting again at boot. Start the service"""
93
97
    upstart_file = os.path.join(init_dir, "{}.conf".format(service_name))
94
98
    sysv_file = os.path.join(initd_dir, service_name)
95
 
    if os.path.exists(upstart_file):
 
99
    if init_is_systemd():
 
100
        service('enable', service_name)
 
101
    elif os.path.exists(upstart_file):
96
102
        override_path = os.path.join(
97
103
            init_dir, '{}.override'.format(service_name))
98
104
        if os.path.exists(override_path):
100
106
    elif os.path.exists(sysv_file):
101
107
        subprocess.check_call(["update-rc.d", service_name, "enable"])
102
108
    else:
103
 
        # XXX: Support SystemD too
104
109
        raise ValueError(
105
 
            "Unable to detect {0} as either Upstart {1} or SysV {2}".format(
 
110
            "Unable to detect {0} as SystemD, Upstart {1} or"
 
111
            " SysV {2}".format(
106
112
                service_name, upstart_file, sysv_file))
107
113
 
108
 
    started = service_start(service_name)
 
114
    started = service_running(service_name)
 
115
    if not started:
 
116
        started = service_start(service_name)
109
117
    return started
110
118
 
111
119
 
112
120
def service(action, service_name):
113
121
    """Control a system service"""
114
 
    cmd = ['service', service_name, action]
 
122
    if init_is_systemd():
 
123
        cmd = ['systemctl', action, service_name]
 
124
    else:
 
125
        cmd = ['service', service_name, action]
115
126
    return subprocess.call(cmd) == 0
116
127
 
117
128
 
118
 
def service_running(service):
 
129
def service_running(service_name):
119
130
    """Determine whether a system service is running"""
120
 
    try:
121
 
        output = subprocess.check_output(
122
 
            ['service', service, 'status'],
123
 
            stderr=subprocess.STDOUT).decode('UTF-8')
124
 
    except subprocess.CalledProcessError:
125
 
        return False
 
131
    if init_is_systemd():
 
132
        return service('is-active', service_name)
126
133
    else:
127
 
        if ("start/running" in output or "is running" in output):
128
 
            return True
129
 
        else:
 
134
        try:
 
135
            output = subprocess.check_output(
 
136
                ['service', service_name, 'status'],
 
137
                stderr=subprocess.STDOUT).decode('UTF-8')
 
138
        except subprocess.CalledProcessError:
130
139
            return False
 
140
        else:
 
141
            if ("start/running" in output or "is running" in output or
 
142
                    "up and running" in output):
 
143
                return True
 
144
            else:
 
145
                return False
131
146
 
132
147
 
133
148
def service_available(service_name):
142
157
        return True
143
158
 
144
159
 
145
 
def adduser(username, password=None, shell='/bin/bash', system_user=False):
146
 
    """Add a user to the system"""
 
160
SYSTEMD_SYSTEM = '/run/systemd/system'
 
161
 
 
162
 
 
163
def init_is_systemd():
 
164
    """Return True if the host system uses systemd, False otherwise."""
 
165
    return os.path.isdir(SYSTEMD_SYSTEM)
 
166
 
 
167
 
 
168
def adduser(username, password=None, shell='/bin/bash', system_user=False,
 
169
            primary_group=None, secondary_groups=None):
 
170
    """Add a user to the system.
 
171
 
 
172
    Will log but otherwise succeed if the user already exists.
 
173
 
 
174
    :param str username: Username to create
 
175
    :param str password: Password for user; if ``None``, create a system user
 
176
    :param str shell: The default shell for the user
 
177
    :param bool system_user: Whether to create a login or system user
 
178
    :param str primary_group: Primary group for user; defaults to username
 
179
    :param list secondary_groups: Optional list of additional groups
 
180
 
 
181
    :returns: The password database entry struct, as returned by `pwd.getpwnam`
 
182
    """
147
183
    try:
148
184
        user_info = pwd.getpwnam(username)
149
185
        log('user {0} already exists!'.format(username))
158
194
                '--shell', shell,
159
195
                '--password', password,
160
196
            ])
 
197
        if not primary_group:
 
198
            try:
 
199
                grp.getgrnam(username)
 
200
                primary_group = username  # avoid "group exists" error
 
201
            except KeyError:
 
202
                pass
 
203
        if primary_group:
 
204
            cmd.extend(['-g', primary_group])
 
205
        if secondary_groups:
 
206
            cmd.extend(['-G', ','.join(secondary_groups)])
161
207
        cmd.append(username)
162
208
        subprocess.check_call(cmd)
163
209
        user_info = pwd.getpwnam(username)
255
301
 
256
302
 
257
303
def fstab_remove(mp):
258
 
    """Remove the given mountpoint entry from /etc/fstab
259
 
    """
 
304
    """Remove the given mountpoint entry from /etc/fstab"""
260
305
    return Fstab.remove_by_mountpoint(mp)
261
306
 
262
307
 
263
308
def fstab_add(dev, mp, fs, options=None):
264
 
    """Adds the given device entry to the /etc/fstab file
265
 
    """
 
309
    """Adds the given device entry to the /etc/fstab file"""
266
310
    return Fstab.add(dev, mp, fs, options=options)
267
311
 
268
312
 
318
362
 
319
363
 
320
364
def file_hash(path, hash_type='md5'):
321
 
    """
322
 
    Generate a hash checksum of the contents of 'path' or None if not found.
 
365
    """Generate a hash checksum of the contents of 'path' or None if not found.
323
366
 
324
367
    :param str hash_type: Any hash alrgorithm supported by :mod:`hashlib`,
325
368
                          such as md5, sha1, sha256, sha512, etc.
334
377
 
335
378
 
336
379
def path_hash(path):
337
 
    """
338
 
    Generate a hash checksum of all files matching 'path'. Standard wildcards
339
 
    like '*' and '?' are supported, see documentation for the 'glob' module for
340
 
    more information.
 
380
    """Generate a hash checksum of all files matching 'path'. Standard
 
381
    wildcards like '*' and '?' are supported, see documentation for the 'glob'
 
382
    module for more information.
341
383
 
342
384
    :return: dict: A { filename: hash } dictionary for all matched files.
343
385
                   Empty if none found.
349
391
 
350
392
 
351
393
def check_hash(path, checksum, hash_type='md5'):
352
 
    """
353
 
    Validate a file using a cryptographic checksum.
 
394
    """Validate a file using a cryptographic checksum.
354
395
 
355
396
    :param str checksum: Value of the checksum used to validate the file.
356
397
    :param str hash_type: Hash algorithm used to generate `checksum`.
365
406
 
366
407
 
367
408
class ChecksumError(ValueError):
 
409
    """A class derived from Value error to indicate the checksum failed."""
368
410
    pass
369
411
 
370
412
 
470
512
 
471
513
 
472
514
def list_nics(nic_type=None):
473
 
    '''Return a list of nics of given type(s)'''
 
515
    """Return a list of nics of given type(s)"""
474
516
    if isinstance(nic_type, six.string_types):
475
517
        int_types = [nic_type]
476
518
    else:
512
554
 
513
555
 
514
556
def set_nic_mtu(nic, mtu):
515
 
    '''Set MTU on a network interface'''
 
557
    """Set the Maximum Transmission Unit (MTU) on a network interface."""
516
558
    cmd = ['ip', 'link', 'set', nic, 'mtu', mtu]
517
559
    subprocess.check_call(cmd)
518
560
 
519
561
 
520
562
def get_nic_mtu(nic):
 
563
    """Return the Maximum Transmission Unit (MTU) for a network interface."""
521
564
    cmd = ['ip', 'addr', 'show', nic]
522
565
    ip_output = subprocess.check_output(cmd).decode('UTF-8').split('\n')
523
566
    mtu = ""
529
572
 
530
573
 
531
574
def get_nic_hwaddr(nic):
 
575
    """Return the Media Access Control (MAC) for a network interface."""
532
576
    cmd = ['ip', '-o', '-0', 'addr', 'show', nic]
533
577
    ip_output = subprocess.check_output(cmd).decode('UTF-8')
534
578
    hwaddr = ""
539
583
 
540
584
 
541
585
def cmp_pkgrevno(package, revno, pkgcache=None):
542
 
    '''Compare supplied revno with the revno of the installed package
 
586
    """Compare supplied revno with the revno of the installed package
543
587
 
544
588
    *  1 => Installed revno is greater than supplied arg
545
589
    *  0 => Installed revno is the same as supplied arg
548
592
    This function imports apt_cache function from charmhelpers.fetch if
549
593
    the pkgcache argument is None. Be sure to add charmhelpers.fetch if
550
594
    you call this function, or pass an apt_pkg.Cache() instance.
551
 
    '''
 
595
    """
552
596
    import apt_pkg
553
597
    if not pkgcache:
554
598
        from charmhelpers.fetch import apt_cache
558
602
 
559
603
 
560
604
@contextmanager
561
 
def chdir(d):
 
605
def chdir(directory):
 
606
    """Change the current working directory to a different directory for a code
 
607
    block and return the previous directory after the block exits. Useful to
 
608
    run commands from a specificed directory.
 
609
 
 
610
    :param str directory: The directory path to change to for this context.
 
611
    """
562
612
    cur = os.getcwd()
563
613
    try:
564
 
        yield os.chdir(d)
 
614
        yield os.chdir(directory)
565
615
    finally:
566
616
        os.chdir(cur)
567
617
 
568
618
 
569
619
def chownr(path, owner, group, follow_links=True, chowntopdir=False):
570
 
    """
571
 
    Recursively change user and group ownership of files and directories
 
620
    """Recursively change user and group ownership of files and directories
572
621
    in given path. Doesn't chown path itself by default, only its children.
573
622
 
 
623
    :param str path: The string path to start changing ownership.
 
624
    :param str owner: The owner string to use when looking up the uid.
 
625
    :param str group: The group string to use when looking up the gid.
574
626
    :param bool follow_links: Also Chown links if True
575
627
    :param bool chowntopdir: Also chown path itself if True
576
628
    """
594
646
 
595
647
 
596
648
def lchownr(path, owner, group):
 
649
    """Recursively change user and group ownership of files and directories
 
650
    in a given path, not following symbolic links. See the documentation for
 
651
    'os.lchown' for more information.
 
652
 
 
653
    :param str path: The string path to start changing ownership.
 
654
    :param str owner: The owner string to use when looking up the uid.
 
655
    :param str group: The group string to use when looking up the gid.
 
656
    """
597
657
    chownr(path, owner, group, follow_links=False)
598
658
 
599
659
 
600
660
def get_total_ram():
601
 
    '''The total amount of system RAM in bytes.
 
661
    """The total amount of system RAM in bytes.
602
662
 
603
663
    This is what is reported by the OS, and may be overcommitted when
604
664
    there are multiple containers hosted on the same machine.
605
 
    '''
 
665
    """
606
666
    with open('/proc/meminfo', 'r') as f:
607
667
        for line in f.readlines():
608
668
            if line: