aboutsummaryrefslogtreecommitdiffstats
path: root/contrail-agent/hooks/charmhelpers/fetch/centos.py
blob: a91dcff0645ed541a79cd72af3112bdff393719a (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
# Copyright 2014-2015 Canonical Limited.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#  http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

import subprocess
import os
import time
import six
import yum

from tempfile import NamedTemporaryFile
from charmhelpers.core.hookenv import log

YUM_NO_LOCK = 1  # The return code for "couldn't acquire lock" in YUM.
YUM_NO_LOCK_RETRY_DELAY = 10  # Wait 10 seconds between apt lock checks.
YUM_NO_LOCK_RETRY_COUNT = 30  # Retry to acquire the lock X times.


def filter_installed_packages(packages):
    """Return a list of packages that require installation."""
    yb = yum.YumBase()
    package_list = yb.doPackageLists()
    temp_cache = {p.base_package_name: 1 for p in package_list['installed']}

    _pkgs = [p for p in packages if not temp_cache.get(p, False)]
    return _pkgs


def install(packages, options=None, fatal=False):
    """Install one or more packages."""
    cmd = ['yum', '--assumeyes']
    if options is not None:
        cmd.extend(options)
    cmd.append('install')
    if isinstance(packages, six.string_types):
        cmd.append(packages)
    else:
        cmd.extend(packages)
    log("Installing {} with options: {}".format(packages,
                                                options))
    _run_yum_command(cmd, fatal)


def upgrade(options=None, fatal=False, dist=False):
    """Upgrade all packages."""
    cmd = ['yum', '--assumeyes']
    if options is not None:
        cmd.extend(options)
    cmd.append('upgrade')
    log("Upgrading with options: {}".format(options))
    _run_yum_command(cmd, fatal)


def update(fatal=False):
    """Update local yum cache."""
    cmd = ['yum', '--assumeyes', 'update']
    log("Update with fatal: {}".format(fatal))
    _run_yum_command(cmd, fatal)


def purge(packages, fatal=False):
    """Purge one or more packages."""
    cmd = ['yum', '--assumeyes', 'remove']
    if isinstance(packages, six.string_types):
        cmd.append(packages)
    else:
        cmd.extend(packages)
    log("Purging {}".format(packages))
    _run_yum_command(cmd, fatal)


def yum_search(packages):
    """Search for a package."""
    output = {}
    cmd = ['yum', 'search']
    if isinstance(packages, six.string_types):
        cmd.append(packages)
    else:
        cmd.extend(packages)
    log("Searching for {}".format(packages))
    result = subprocess.check_output(cmd)
    for package in list(packages):
        output[package] = package in result
    return output


def add_source(source, key=None):
    """Add a package source to this system.

    @param source: a URL with a rpm package

    @param key: A key to be added to the system's keyring and used
    to verify the signatures on packages. Ideally, this should be an
    ASCII format GPG public key including the block headers. A GPG key
    id may also be used, but be aware that only insecure protocols are
    available to retrieve the actual public key from a public keyserver
    placing your Juju environment at risk.
    """
    if source is None:
        log('Source is not present. Skipping')
        return

    if source.startswith('http'):
        directory = '/etc/yum.repos.d/'
        for filename in os.listdir(directory):
            with open(directory + filename, 'r') as rpm_file:
                if source in rpm_file.read():
                    break
        else:
            log("Add source: {!r}".format(source))
            # write in the charms.repo
            with open(directory + 'Charms.repo', 'a') as rpm_file:
                rpm_file.write('[%s]\n' % source[7:].replace('/', '_'))
                rpm_file.write('name=%s\n' % source[7:])
                rpm_file.write('baseurl=%s\n\n' % source)
    else:
        log("Unknown source: {!r}".format(source))

    if key:
        if '-----BEGIN PGP PUBLIC KEY BLOCK-----' in key:
            with NamedTemporaryFile('w+') as key_file:
                key_file.write(key)
                key_file.flush()
                key_file.seek(0)
                subprocess.check_call(['rpm', '--import', key_file.name])
        else:
            subprocess.check_call(['rpm', '--import', key])


def _run_yum_command(cmd, fatal=False):
    """Run an YUM command.

    Checks the output and retry if the fatal flag is set to True.

    :param: cmd: str: The yum command to run.
    :param: fatal: bool: Whether the command's output should be checked and
        retried.
    """
    env = os.environ.copy()

    if fatal:
        retry_count = 0
        result = None

        # If the command is considered "fatal", we need to retry if the yum
        # lock was not acquired.

        while result is None or result == YUM_NO_LOCK:
            try:
                result = subprocess.check_call(cmd, env=env)
            except subprocess.CalledProcessError as e:
                retry_count = retry_count + 1
                if retry_count > YUM_NO_LOCK_RETRY_COUNT:
                    raise
                result = e.returncode
                log("Couldn't acquire YUM lock. Will retry in {} seconds."
                    "".format(YUM_NO_LOCK_RETRY_DELAY))
                time.sleep(YUM_NO_LOCK_RETRY_DELAY)

    else:
        subprocess.call(cmd, env=env)