Passed
Pull Request — master (#140)
by
unknown
01:47
created

gvmtools.parser.create_connection()   A

Complexity

Conditions 3

Size

Total Lines 26
Code Lines 22

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 3
eloc 22
nop 11
dl 0
loc 26
rs 9.352
c 0
b 0
f 0

How to fix   Many Parameters   

Many Parameters

Methods with many parameters are not only hard to understand, but their parameters also often become inconsistent when you need more, or different data.

There are several approaches to avoid long parameter lists:

1
# -*- coding: utf-8 -*-
2
# Copyright (C) 2018 Greenbone Networks GmbH
3
#
4
# SPDX-License-Identifier: GPL-3.0-or-later
5
#
6
# This program is free software: you can redistribute it and/or modify
7
# it under the terms of the GNU General Public License as published by
8
# the Free Software Foundation, either version 3 of the License, or
9
# (at your option) any later version.
10
#
11
# This program is distributed in the hope that it will be useful,
12
# but WITHOUT ANY WARRANTY; without even the implied warranty of
13
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14
# GNU General Public License for more details.
15
#
16
# You should have received a copy of the GNU General Public License
17
# along with this program.  If not, see <http://www.gnu.org/licenses/>.
18
19
"""Command Line Interface Parser
20
"""
21
22
import argparse
23
import configparser
24
import logging
25
import os
26
27
from gvm import get_version as get_gvm_version
28
from gvm.connections import (DEFAULT_UNIX_SOCKET_PATH,
29
                             DEFAULT_TIMEOUT,
30
                             DEFAULT_GVM_PORT,
31
                             SSHConnection,
32
                             TLSConnection,
33
                             UnixSocketConnection)
34
35
from gvmtools import get_version
36
37
logger = logging.getLogger(__name__)
38
39
__version__ = get_version()
40
__api_version__ = get_gvm_version()
41
42
DEFAULT_CONFIG_PATH = '~/.config/gvm-tools.conf'
43
44
PROTOCOL_OSP = 'OSP'
45
PROTOCOL_GMP = 'GMP'
46
DEFAULT_PROTOCOL = PROTOCOL_GMP
47
48
49
class CliParser:
50
51
    def __init__(self, description, logfilename):
52
        root_parser = argparse.ArgumentParser(
53
            description=description,
54
            formatter_class=argparse.RawTextHelpFormatter,
55
            add_help=False)
56
57
        root_parser.add_argument(
58
            '-c', '--config', nargs='?', default=DEFAULT_CONFIG_PATH,
59
            help='Configuration file path (default: %(default)s)')
60
        root_parser.add_argument(
61
            '--log', nargs='?', dest='loglevel', const='INFO',
62
            choices=['DEBUG', 'INFO', 'WARNING', 'ERROR', 'CRITICAL'],
63
            help='Activate logging (default level: %(default)s)')
64
65
        args_before, _ = root_parser.parse_known_args()
66
67
        if args_before.loglevel is not None:
68
            level = logging.getLevelName(args_before.loglevel)
69
            logging.basicConfig(filename=logfilename, level=level)
70
71
        root_parser.add_argument(
72
            '--timeout', required=False, default=DEFAULT_TIMEOUT, type=int,
73
            help='Response timeout in seconds, or -1 to wait '
74
                 'indefinitely (default: %(default)s)')
75
        root_parser.add_argument(
76
            '--gmp-username',
77
            help='Username for GMP service (default: %(default)r)')
78
        root_parser.add_argument(
79
            '--gmp-password',
80
            help='Password for GMP service (default: %(default)r)')
81
        root_parser.add_argument(
82
            '-V', '--version', action='version',
83
            version='%(prog)s {version} (API version {apiversion})'.format(
84
                version=__version__, apiversion=__api_version__),
85
            help='Show version information and exit')
86
87
        parser = argparse.ArgumentParser(parents=[root_parser])
88
89
        subparsers = parser.add_subparsers(
90
            metavar='CONNECTION_TYPE',
91
            title='connections',
92
            description='valid connection types',
93
            help="Connection type to use",
94
        )
95
        subparsers.required = True
96
        subparsers.dest = 'connection_type'
97
98
        self._config = self._load_config(args_before.config)
99
100
        self._parser = parser
101
        self._root_parser = root_parser
102
        self._subparsers = subparsers
103
104
    def parse_args(self):
105
        self._add_subparsers()
106
107
        self._parser.set_defaults(
108
            gmp_username=self._config.get('gmp', 'username', fallback=''),
109
            gmp_password=self._config.get('gmp', 'password', fallback=''),
110
            **self._config.defaults()
111
        )
112
113
        args = self._parser.parse_args()
114
115
        # If timeout value is -1, then the socket should have no timeout
116
        if args.timeout == -1:
117
            args.timeout = None
118
119
120
        logging.debug('Parsed arguments %r', args)
121
122
        return args
123
124
    def add_argument(self, *args, **kwargs):
125
        self._parser.add_argument(*args, **kwargs)
126
        self._root_parser.add_argument(*args, **kwargs)
127
128
    def _load_config(self, configfile):
129
        config = configparser.ConfigParser(default_section='main')
130
131
        try:
132
            path = os.path.expanduser(configfile)
133
            config.read(path)
134
        except Exception as e: # pylint: disable=broad-except
135
            raise RuntimeError(
136
                'Error while parsing config file {config}. Error was '
137
                '{message}'.format(config=configfile, message=e))
138
139
        logger.debug('Loaded config %s', configfile)
140
        return config
141
142
    def _add_subparsers(self):
143
        parser_ssh = self._subparsers.add_parser(
144
            'ssh', help='Use SSH to connect to service',
145
            parents=[self._root_parser])
146
147
        parser_ssh.add_argument('--hostname', required=True,
148
                                help='Hostname or IP address')
149
        parser_ssh.add_argument('--port', required=False,
150
                                help='SSH port (default: %(default)s)')
151
        parser_ssh.add_argument('--ssh-username',
152
                                help='SSH username (default: %(default)r)')
153
        parser_ssh.add_argument('--ssh-password',
154
                                help='SSH password (default: %(default)r)')
155
156
        parser_ssh.set_defaults(
157
            port=int(self._config.get('ssh', 'port', fallback=22)),
158
            ssh_username=self._config.get('ssh', 'username', fallback='gmp'),
159
            ssh_password=self._config.get('ssh', 'password', fallback='gmp'),
160
        )
161
162
        parser_tls = self._subparsers.add_parser(
163
            'tls', help='Use TLS secured connection to connect to service',
164
            parents=[self._root_parser])
165
        parser_tls.add_argument('--hostname', required=True,
166
                                help='Hostname or IP address')
167
        parser_tls.add_argument('--port', required=False,
168
                                help='GMP/OSP port (default: %(default)s)')
169
        parser_tls.add_argument(
170
            '--certfile', required=False,
171
            help='Path to the certificate file for client authentication. '
172
                 '(default: %(default)s)')
173
        parser_tls.add_argument(
174
            '--keyfile', required=False,
175
            help='Path to key file for client authentication. '
176
                 '(default: %(default)s)')
177
        parser_tls.add_argument(
178
            '--cafile', required=False,
179
            help='Path to CA certificate for server authentication. '
180
                 '(default: %(default)s)')
181
        parser_tls.add_argument(
182
            '--no-credentials', required=False, default=False,
183
            help='Use only certificates for authentication')
184
        parser_tls.set_defaults(
185
            port=int(self._config.get(
186
                'tls', 'port', fallback=DEFAULT_GVM_PORT)),
187
            certfile=self._config.get('tls', 'certfile', fallback=None),
188
            keyfile=self._config.get('tls', 'keyfile', fallback=None),
189
            cafile=self._config.get('tls', 'cafile', fallback=None),
190
        )
191
192
        parser_socket = self._subparsers.add_parser(
193
            'socket', help='Use UNIX Domain socket to connect to service',
194
            parents=[self._root_parser])
195
196
        socketpath_group = parser_socket.add_mutually_exclusive_group()
197
        socketpath_group.add_argument(
198
            '--sockpath', nargs='?', default=None,
199
            help='Deprecated, use --socketpath instead')
200
        socketpath_group.add_argument(
201
            '--socketpath', nargs='?',
202
            help='Path to UNIX Domain socket (default: %(default)s)')
203
204
        parser_socket.set_defaults(
205
            socketpath=self._config.get(
206
                'unixsocket', 'socketpath', fallback=DEFAULT_UNIX_SOCKET_PATH),
207
        )
208
209
    def add_protocol_argument(self):
210
        self.add_argument(
211
            '--protocol', required=False, default=DEFAULT_PROTOCOL,
212
            choices=[PROTOCOL_GMP, PROTOCOL_OSP],
213
            help='Service protocol to use (default: %(default)s)')
214
215
216
def create_parser(description, logfilename):
217
    return CliParser(description, logfilename)
218
219
220
def create_connection(connection_type, socketpath=None, timeout=None,
221
                      hostname=None, port=None, certfile=None, keyfile=None,
222
                      cafile=None, ssh_username=None, ssh_password=None,
223
                      **kwargs):
224
    if 'socket' in connection_type:
225
        return UnixSocketConnection(
226
            timeout=timeout,
227
            path=socketpath
228
        )
229
230
    if 'tls' in connection_type:
231
        return TLSConnection(
232
            timeout=timeout,
233
            hostname=hostname,
234
            port=port,
235
            certfile=certfile,
236
            keyfile=keyfile,
237
            cafile=cafile,
238
        )
239
240
    return SSHConnection(
241
        timeout=timeout,
242
        hostname=hostname,
243
        port=port,
244
        username=ssh_username,
245
        password=ssh_password
246
    )
247