~ubuntu-core-dev/update-manager/main

2727.1.1 by Brian Murray
Include HWE support tools and information. (LP: #1498059)
1
#!/usr/bin/python
2
3
from __future__ import print_function
4
5
import optparse
6
import datetime
7
import distro_info
8
import os
9
import re
10
import subprocess
11
import sys
12
13
import apt
14
from UpdateManager.Core.utils import twrap, get_dist
15
16
# set locale early so that the subsequent imports have localized
17
# strings
18
import locale
19
try:
20
    locale.setlocale(locale.LC_ALL, "")
21
except:
22
    pass
23
24
from gettext import gettext as _
25
import gettext
26
gettext.textdomain("update-manager")
27
28
29
from HweSupportStatus.consts import (
30
    Messages,
31
    LTS_EOL_DATE,
32
    HWE_EOL_DATE,
33
    NEXT_LTS_DOT1_DATE,
34
)
35
36
37
# HWE stack with a short support period
38
HWE_UNSUPPORTED_BACKPORTS = (
39
    "-lts-utopic",
40
    "-lts-vivid",
41
    "-lts-wily"
42
)
43
# from https://wiki.ubuntu.com/Kernel/LTSEnablementStack
2735 by Brian Murray
hwe-support-status: Do not show replacements that are already installed on
44
UNSUPPORTED_KERNEL_IMAGE_REGEX = \
45
    r'linux-image.*-(3\.16|3\.19|4\.2)(\.[0-9]+)?-.*'
2727.1.1 by Brian Murray
Include HWE support tools and information. (LP: #1498059)
46
47
# HWE stack with a long support period
2946 by Łukasz 'sil2100' Zemczak
Adjust dates in hwe-support-status for jammy.
48
HWE_SUPPORTED_BACKPORT = "-hwe-22.04"
2879 by Łukasz 'sil2100' Zemczak
Adjust dates in hwe-support-status for focal.
49
SUPPORTED_KERNEL_IMAGE_REGEX = r'^$' # No fixed backported kernel yet
2727.1.1 by Brian Murray
Include HWE support tools and information. (LP: #1498059)
50
51
52
KERNEL_METAPKGS = (
53
    "linux-generic",
54
    "linux-image-generic",
55
    "linux-signed-generic",
56
    "linux-signed-image-generic",
57
)
58
XORG_METAPKGS = (
59
    "xserver-xorg",
60
    "libgl1-mesa-glx",
2739 by Brian Murray
add libwayland-egl1-mesa to the list of metapackages.
61
    # LP: #1610434 - Ubuntu GNOME needed libwayland
62
    "libwayland-egl1-mesa",
2727.1.1 by Brian Murray
Include HWE support tools and information. (LP: #1498059)
63
)
2740 by Brian Murray
utilize a virtualbox metapackage set.
64
VBOX_METAPKGS = (
65
    "virtualbox-guest-utils",
66
    "virtualbox-guest-source"
67
)
68
METAPKGS = KERNEL_METAPKGS + XORG_METAPKGS + VBOX_METAPKGS
2727.1.1 by Brian Murray
Include HWE support tools and information. (LP: #1498059)
69
70
71
class Package:
72
    """A lightweight apt package """
73
    def __init__(self, name, version, arch, foreign=False):
74
        self.name = name
75
        self.installed_version = version
76
        self.arch = arch
77
        self.foreign = foreign
78
79
80
def find_hwe_packages(installed_packages):
81
    unsupported_hwe_packages = set()
82
    supported_hwe_packages = set()
83
    for pkg in installed_packages:
84
        # metapackages and X are marked with the -lts-$distro string
85
        for name in HWE_UNSUPPORTED_BACKPORTS:
86
            if pkg.name.endswith(name):
87
                unsupported_hwe_packages.add(pkg)
2735 by Brian Murray
hwe-support-status: Do not show replacements that are already installed on
88
        # The individual backported kernels have names like
2727.1.1 by Brian Murray
Include HWE support tools and information. (LP: #1498059)
89
        #   linux-image-3.11.0-17-generic
90
        # so we match via a regexp.
2735 by Brian Murray
hwe-support-status: Do not show replacements that are already installed on
91
        #
2727.1.1 by Brian Murray
Include HWE support tools and information. (LP: #1498059)
92
        # The linux-image-generic-lts-$distro metapkg has additional
93
        #  dependencies (like linux-firmware) so we can't just walk the
94
        # dependency chain.
95
        if re.match(UNSUPPORTED_KERNEL_IMAGE_REGEX, pkg.name):
96
            unsupported_hwe_packages.add(pkg)
97
        # SUPPORTED
98
        if pkg.name.endswith(HWE_SUPPORTED_BACKPORT):
99
            supported_hwe_packages.add(pkg)
100
        if re.match(SUPPORTED_KERNEL_IMAGE_REGEX, pkg.name):
101
            supported_hwe_packages.add(pkg)
102
    return unsupported_hwe_packages, supported_hwe_packages
103
104
105
def is_unsupported_hwe_kernel_running(unsupported_hwe_package):
106
    # kernels do not conflict with each other, so we need to check
107
    # what version is actually running
108
    running_kernel_ver = os.uname()[2]
109
    # the running kernel without the abi or buildver
110
    running_kernel_ver = running_kernel_ver.split("-")[0]
111
    for pkg in unsupported_hwe_package:
112
        if not pkg.name.startswith("linux-"):
113
            continue
114
        # we only care about the version, not abi or build
115
        if pkg.installed_version.startswith(running_kernel_ver):
116
            return True
117
    return False
118
119
120
def is_unsupported_xstack_running(unsupported_hwe_packages):
121
    # the HWE xstacks conflict with each other, so we can simply test
122
    # for existence in the installed unsupported hwe packages
123
    for pkg in unsupported_hwe_packages:
124
        for xorg_meta in XORG_METAPKGS:
125
            if pkg.name.startswith(xorg_meta):
126
                return True
127
    return False
128
129
2735 by Brian Murray
hwe-support-status: Do not show replacements that are already installed on
130
def find_supported_replacement_hwe_packages(unsupported_hwe_packages,
131
                                            installed_packages):
2727.1.1 by Brian Murray
Include HWE support tools and information. (LP: #1498059)
132
    unsupported_metapkg_names = set()
133
    replacement_names = set()
134
    for metapkg in METAPKGS:
135
        for unsupported_backport in HWE_UNSUPPORTED_BACKPORTS:
136
            metapkg_name = metapkg + unsupported_backport
137
            for pkg in unsupported_hwe_packages:
138
                if pkg.name == metapkg_name:
139
                    replacement_name = metapkg + HWE_SUPPORTED_BACKPORT
2735 by Brian Murray
hwe-support-status: Do not show replacements that are already installed on
140
                    if (replacement_name, pkg.arch) not in \
141
                            [(p.name, p.arch) for p in installed_packages]:
142
                        if pkg.foreign:
143
                            replacement_name += ':' + pkg.arch
144
                        replacement_names.add(replacement_name)
2727.1.1 by Brian Murray
Include HWE support tools and information. (LP: #1498059)
145
                    unsupported_metapkg_names.add(metapkg_name)
146
    return unsupported_metapkg_names, replacement_names
147
148
149
def is_unsupported_hwe_running(unsupported_hwe_packages):
150
    return (is_unsupported_hwe_kernel_running(unsupported_hwe_packages) or
151
            is_unsupported_xstack_running(unsupported_hwe_packages))
152
153
154
def advice_about_hwe_status(unsupported_hwe_packages, supported_hwe_packages,
2735 by Brian Murray
hwe-support-status: Do not show replacements that are already installed on
155
                            installed_packages, has_update_manager, today,
156
                            verbose):
2727.1.1 by Brian Murray
Include HWE support tools and information. (LP: #1498059)
157
    unsupported_hwe_stack_running = is_unsupported_hwe_running(
158
        unsupported_hwe_packages)
159
    unsupported_hwe_metapkgs, supported_replacement_hwe = \
2735 by Brian Murray
hwe-support-status: Do not show replacements that are already installed on
160
        find_supported_replacement_hwe_packages(unsupported_hwe_packages,
161
                                                installed_packages)
2727.1.1 by Brian Murray
Include HWE support tools and information. (LP: #1498059)
162
    # we need the "-p" option until the next LTS point release is available
163
    if today < NEXT_LTS_DOT1_DATE:
164
        do_release_upgrade_option = "-p"
165
    else:
166
        do_release_upgrade_option = ""
167
168
    if unsupported_hwe_stack_running:
169
        if today < HWE_EOL_DATE:
170
            s = Messages.HWE_SUPPORT_ENDS
171
        else:
172
            s = Messages.HWE_SUPPORT_HAS_ENDED
173
        if has_update_manager:
174
            print(s + Messages.UM_UPGRADE)
175
        else:
176
            # bug #1341320 - if no metapkg is left we need to show
177
            #                what is no longer supported
178
            if supported_replacement_hwe:
179
                print(s + Messages.APT_UPGRADE % (
180
                    do_release_upgrade_option,
181
                    " ".join(supported_replacement_hwe)))
182
            else:
183
                print(s + Messages.APT_SHOW_UNSUPPORTED % (
184
                    " ".join([pkg.name for pkg in unsupported_hwe_packages])))
185
186
    # some unsupported package installed but not running and not superseded
187
    # - this is worth reporting
188
    elif (unsupported_hwe_packages and
189
          not supported_hwe_packages and
190
          not unsupported_hwe_stack_running):
191
        s = _("""
192
You have packages from the Hardware Enablement Stack (HWE) installed that
193
are going out of support on %s.
194
        """) % HWE_EOL_DATE
195
        if has_update_manager:
196
            print(s + Messages.UM_UPGRADE)
197
        else:
198
            print(s + Messages.APT_UPGRADE % (
199
                do_release_upgrade_option,
200
                " ".join(supported_replacement_hwe)))
201
    elif supported_hwe_packages:
202
        print(Messages.HWE_SUPPORTED)
203
    elif verbose:
204
        print(
205
            _("You are not running a system with a Hardware Enablement Stack. "
206
              "Your system is supported until %(month)s %(year)s.") % {
207
                  'month': LTS_EOL_DATE.strftime("%B"),
208
                  'year': LTS_EOL_DATE.year})
209
210
211
if __name__ == "__main__":
212
    parser = optparse.OptionParser(description=_("Check HWE support status"))
213
    parser.add_option('--quiet', action='store_true', default=False,
2735 by Brian Murray
hwe-support-status: Do not show replacements that are already installed on
214
                      help="No output, exit code 10 on unsupported HWE "
215
                      "packages")
2727.1.1 by Brian Murray
Include HWE support tools and information. (LP: #1498059)
216
    parser.add_option('--verbose', action='store_true', default=False,
2735 by Brian Murray
hwe-support-status: Do not show replacements that are already installed on
217
                      help="more verbose output")
2727.1.1 by Brian Murray
Include HWE support tools and information. (LP: #1498059)
218
    parser.add_option('--show-all-unsupported', action='store_true',
2735 by Brian Murray
hwe-support-status: Do not show replacements that are already installed on
219
                      default=False,
220
                      help="Show unsupported HWE packages")
2727.1.1 by Brian Murray
Include HWE support tools and information. (LP: #1498059)
221
    parser.add_option('--show-replacements', action='store_true',
222
                      default=False,
2735 by Brian Murray
hwe-support-status: Do not show replacements that are already installed on
223
                      help="show what packages need installing to be "
224
                      "supported")
2727.1.1 by Brian Murray
Include HWE support tools and information. (LP: #1498059)
225
    # hidden, only useful for testing
226
    parser.add_option(
227
        '--disable-hwe-check-semaphore-file',
228
        default="/var/lib/update-notifier/disable-hwe-eol-messages",
229
        help=optparse.SUPPRESS_HELP)
230
    options, args = parser.parse_args()
231
232
    if options.quiet:
233
        nullfd = os.open(os.devnull, os.O_WRONLY)
234
        os.dup2(nullfd, sys.stdout.fileno())
235
236
    # Check to see if we are an LTS release
237
    di = distro_info.UbuntuDistroInfo()
238
    codename = get_dist()
239
    lts = di.is_lts(codename)
240
    if not lts:
241
        if options.verbose:
242
            print("Only LTS releases have Hardware Enablement stacks",
243
                  file=sys.stderr)
244
        sys.exit(0)
245
246
    # request from PSE to be able to disable the hwe check via a special
247
    # semaphore file
248
    HWE_CHECK_DISABLED_FILE = options.disable_hwe_check_semaphore_file
249
    if os.path.exists(HWE_CHECK_DISABLED_FILE):
250
        if options.verbose:
251
            print("Forcefully disabled hwe-support-status via file %s" %
252
                  HWE_CHECK_DISABLED_FILE, file=sys.stderr)
253
        sys.exit(0)
254
255
    foreign_archs = set(subprocess.check_output(
2738 by Brian Murray
hwe-support-status: decode output when checking for foreign architectures
256
        ['dpkg', '--print-foreign-architectures'],
257
        universal_newlines=True).split())
2727.1.1 by Brian Murray
Include HWE support tools and information. (LP: #1498059)
258
259
    # do the actual check
260
    installed_packages = set()
261
    today = datetime.date.today()
262
    tagf = apt.apt_pkg.TagFile("/var/lib/dpkg/status")
263
    while tagf.step():
264
        if tagf.section.find("Status", "") != "install ok installed":
265
            continue
266
        pkgname = tagf.section.find("Package")
267
        version = tagf.section.find("Version")
268
        arch = tagf.section.find("Architecture")
269
        foreign = arch in foreign_archs
270
        installed_packages.add(Package(pkgname, version, arch, foreign))
271
272
    has_update_manager = "update-manager" in [
273
        pkg.name for pkg in installed_packages]
274
    unsupported_hwe_packages, supported_hwe_packages = find_hwe_packages(
275
        installed_packages)
276
277
    if options.show_all_unsupported:
278
        if today > HWE_EOL_DATE:
279
            print(twrap(" ".join([
2735 by Brian Murray
hwe-support-status: Do not show replacements that are already installed on
280
                pkg.foreign and pkg.name + ':' + pkg.arch or pkg.name
2727.1.1 by Brian Murray
Include HWE support tools and information. (LP: #1498059)
281
                        for pkg in unsupported_hwe_packages])))
282
283
    if options.show_replacements:
2735 by Brian Murray
hwe-support-status: Do not show replacements that are already installed on
284
        unsupported, replacements = find_supported_replacement_hwe_packages(
285
            unsupported_hwe_packages, installed_packages)
286
        if replacements:
287
            print(" ".join(replacements))
2727.1.1 by Brian Murray
Include HWE support tools and information. (LP: #1498059)
288
289
    if not options.show_all_unsupported and not options.show_replacements:
290
        advice_about_hwe_status(
291
            unsupported_hwe_packages, supported_hwe_packages,
2736 by Brian Murray
fix passing of installed_packages
292
            installed_packages, has_update_manager, today,
293
            options.verbose)
2735 by Brian Murray
hwe-support-status: Do not show replacements that are already installed on
294
    if is_unsupported_hwe_running(unsupported_hwe_packages) and \
295
            today > HWE_EOL_DATE:
2727.1.1 by Brian Murray
Include HWE support tools and information. (LP: #1498059)
296
        sys.exit(10)
297
298
    sys.exit(0)