File: ip.py

package info (click to toggle)
netplan.io 1.2-1
  • links: PTS, VCS
  • area: main
  • in suites: experimental
  • size: 4,268 kB
  • sloc: python: 34,640; ansic: 14,096; xml: 4,989; javascript: 2,165; sh: 513; makefile: 118
file content (158 lines) | stat: -rw-r--r-- 6,595 bytes parent folder | download
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
#!/usr/bin/python3
#
# Copyright (C) 2018 Canonical, Ltd.
# Author: Mathieu Trudel-Lapierre <mathieu.trudel-lapierre@canonical.com>
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; version 3.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program.  If not, see <http://www.gnu.org/licenses/>.

'''netplan ip command line'''

import logging
import os
import sys
import subprocess
from subprocess import CalledProcessError

from .. import utils

lease_path = {
    'networkd': {
        'pattern': 'run/systemd/netif/leases/{lease_id}',
        'method': 'ifindex',
    },
    'NetworkManager': {
        'pattern': 'var/lib/NetworkManager/internal-{lease_id}-{interface}.lease',
        'method': 'nm_connection',
    },
}


class NetplanIp(utils.NetplanCommand):

    def __init__(self):
        super().__init__(command_id='ip',
                         description='Retrieve IP information from the system',
                         leaf=False)

    def run(self):
        self.command_leases = NetplanIpLeases()

        # subcommand: leases
        p_ip_leases = self.subparsers.add_parser('leases',
                                                 help='Display IP leases',
                                                 add_help=False)
        p_ip_leases.set_defaults(func=self.command_leases.run, commandclass=self.command_leases)

        self.parse_args()
        self.run_command()


class NetplanIpLeases(utils.NetplanCommand):

    def __init__(self):
        super().__init__(command_id='ip leases',
                         description='Display IP leases',
                         leaf=True)

    def run(self):
        self.parser.add_argument('interface',
                                 help='Interface for which to display IP lease settings.')
        self.parser.add_argument('--root-dir',
                                 help='Search for configuration files in this root directory instead of /')

        self.func = self.command_ip_leases

        self.parse_args()
        self.run_command()

    def command_ip_leases(self):

        if self.interface == 'help':  # pragma: nocover (covered in autopkgtest)
            self.print_usage()

        def find_lease_file(mapping):
            def lease_method_ifindex():
                ifindex_f = os.path.join('/sys/class/net', self.interface, 'ifindex')
                try:
                    with open(ifindex_f) as f:
                        return f.readlines()[0].strip()
                except Exception as e:
                    logging.debug('Cannot read file %s: %s', ifindex_f, str(e))
                    raise

            def lease_method_nm_connection():
                # FIXME: handle older versions of NM where 'nmcli dev show' doesn't exist
                try:
                    nmcli_dev_out = utils.nmcli_out(['dev', 'show', self.interface])
                    for line in nmcli_dev_out.splitlines():
                        if 'GENERAL.CONNECTION' in line:
                            conn_id = line.split(':')[1].rstrip().strip()
                            nmcli_con_out = utils.nmcli_out(['con', 'show', 'id', conn_id])
                            for line in nmcli_con_out.splitlines():
                                if 'connection.uuid' in line:
                                    return line.split(':')[1].rstrip().strip()
                except Exception as e:
                    raise Exception('Could not find a NetworkManager connection for the interface: %s' % str(e))
                raise Exception('Could not find a NetworkManager connection for the interface')

            lease_pattern = lease_path[mapping['backend']]['pattern']
            lease_method = lease_path[mapping['backend']]['method']

            try:
                lease_id = eval("lease_method_" + lease_method)()

                # We found something to build the path to the lease file with,
                # at this point we may have something to look at; but if not,
                # we'll rely on open() throwing an error.
                # This might happen if networkd doesn't use DHCP for the interface,
                # for instance.
                path = os.path.join('/',
                                    os.path.abspath(self.root_dir) if self.root_dir else "",
                                    lease_pattern.format(interface=self.interface,
                                                         lease_id=lease_id))
                # Fallback to 'dhclient' if no lease of NetworkManager's
                # internal DHCP client is found
                if not os.path.isfile(path):
                    path = path.replace('NetworkManager/internal-', 'NetworkManager/dhclient-')
                with open(path) as f:
                    for line in f.readlines():
                        print(line.rstrip())
            except Exception as e:
                print("No lease found for interface '%s': %s" % (self.interface, str(e)),
                      file=sys.stderr)
                sys.exit(1)

        # XXX: This code path is only still supported for legacy reasons and
        # not supposed to be called in the scope of a systemd generator. The
        # 'netplan status' command should be used instead. Should it be moved to
        # the 'configure' binary to keep legacy functionality around?
        # See also the comment in main() in src/generate.c.
        argv = [utils.get_generator_path()]
        if self.root_dir:
            argv += ['--root-dir', self.root_dir]
        argv += ['--mapping', self.interface]

        # Extract out of the generator our mapping in a dict.
        logging.debug('command ip leases: running %s', argv)
        try:
            out = subprocess.check_output(argv, text=True)
        except CalledProcessError:  # pragma: nocover (better be covered in autopkgtest)
            print("No lease found for interface '%s' (not managed by Netplan)" % self.interface, file=sys.stderr)
            sys.exit(1)
        mapping = {}
        mapping_s = out.split(',')
        for keyvalue in mapping_s:
            key, value = keyvalue.strip().split('=')
            mapping[key] = value

        find_lease_file(mapping)