Skip to content
This repository was archived by the owner on Jun 14, 2022. It is now read-only.

Commit 215852f

Browse files
Andrea Bettichsliverc
authored andcommitted
Changed hostname recognition from dhclient to nmcli
1 parent 2fcc23e commit 215852f

13 files changed

+585
-92
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,3 +2,4 @@
22
*.swp
33
site/
44
.python-version
5+
.cache/

.travis.yml

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
language: python
2+
# Python Version
3+
python:
4+
- "2.7"
5+
# Ignore submodules
6+
git:
7+
submodules: false
8+
# Install requirements
9+
install: "pip install -r requirements.txt"
10+
# Run pytests
11+
script: pytest

requirements.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
pytest-mock

virtesk-tc-connectspice/conftest.py

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
#!/usr/bin/env python
2+
# -*- coding: UTF-8 -*-
3+
# vim: autoindent expandtab tabstop=4 sw=4 sts=4 filetype=python
4+
5+
# Copyright (c) 2013-2016, Adfinis SyGroup AG
6+
#
7+
# This file is part of Virtesk VDI.
8+
#
9+
# Virtesk VDI is free software: you can redistribute it and/or modify
10+
# it under the terms of the GNU General Public License as published by
11+
# the Free Software Foundation, either version 3 of the License, or
12+
# (at your option) any later version.
13+
#
14+
# Virtesk VDI is distributed in the hope that it will be useful,
15+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
16+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17+
# GNU General Public License for more details.
18+
#
19+
# You should have received a copy of the GNU General Public License
20+
# along with Virtesk VDI. If not, see <http://www.gnu.org/licenses/>.
21+
22+
# System Imports
23+
import pytest
24+
import os.path
25+
26+
27+
MOCK_DATA_PATH = os.path.join(os.path.dirname(os.path.realpath(__file__)), 'test_mock_data')
28+
29+
30+
@pytest.yield_fixture(scope='module')
31+
def active_connection():
32+
yield readfile('active_connection')
33+
34+
35+
@pytest.yield_fixture(scope='module')
36+
def nmcli_con_show_empty():
37+
yield readfile('nmcli_con_show_adsy_eap_empty')
38+
39+
40+
@pytest.yield_fixture(scope='module')
41+
def nmcli_con_show():
42+
yield readfile('nmcli_con_show_adsy_eap')
43+
44+
45+
@pytest.yield_fixture(scope='module')
46+
def nmcli_device_show_single_line():
47+
yield readfile('nmcli_device_show_single_line')
48+
49+
50+
@pytest.yield_fixture(scope='module')
51+
def nmcli_device_show():
52+
yield readfile('nmcli_device_show')
53+
54+
55+
def readfile(filename):
56+
with open(os.path.join(MOCK_DATA_PATH, filename), 'r') as f:
57+
return f.read()

virtesk-tc-connectspice/connect_spice_client.py

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@
3939
# Project Imports
4040
import spice_xpi_controller
4141
import find_thinclient_identifier
42+
import find_thinclient_identifier_nmcli
4243
import ovirtsdk.api
4344

4445

@@ -632,16 +633,15 @@ def raise_exception_again_in_main_action_sequence(self):
632633
self.raise_again = None
633634
raise ex
634635

635-
def wait_for_dhcp_lease(self):
636-
logging.info("waiting for dhcp lease file...")
637-
for iteration in range(1, 5 + 1):
638-
leasefiles = find_thinclient_identifier.find_dhclient_leasefiles()
639-
if len(leasefiles) > 0:
640-
logging.info("waiting for dhcp lease file... done")
636+
def wait_for_active_connection(self):
637+
logging.info("waiting for active connection")
638+
for iteration in range(0, 5):
639+
active_connections = find_thinclient_identifier_nmcli.get_active_connections()
640+
if active_connections > 0:
641+
logging.info("found an active connection")
641642
return
642643
self.notify_waiting_for_dhcplease()
643644
time.sleep(4)
644-
645645
# TRANSLATE:
646646
# "Network error. Please contact the system administrator."
647647
raise NetworkError(
@@ -657,7 +657,7 @@ def main_action_sequence(self):
657657

658658
self.adjust_logging()
659659

660-
self.wait_for_dhcp_lease()
660+
self.wait_for_active_connection()
661661

662662
self.connect_to_rest_api()
663663

@@ -683,7 +683,7 @@ def shutdown_vm_action_sequence(self):
683683

684684
logging.info("Auto-Shutdown program started...")
685685

686-
self.wait_for_dhcp_lease()
686+
self.wait_for_active_connection()
687687

688688
self.connect_to_rest_api()
689689

virtesk-tc-connectspice/find_thinclient_identifier.py

Lines changed: 3 additions & 83 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
import re
2525
import logging
2626
import subprocess
27+
from find_thinclient_identifier_nmcli import extract_identifiers_from_nmcli
2728

2829

2930
def get_dmidecode_sysuuid():
@@ -37,94 +38,14 @@ def get_dmidecode_sysuuid():
3738
return result
3839

3940

40-
def find_dhclient_leasefiles():
41-
# Finds dhclient lease files.
42-
# More or less the same as:
43-
# ps ax | grep dhclient
44-
# and then looking at the -lf parameter.
45-
46-
procdir = os.listdir('/proc')
47-
48-
leasefiles = []
49-
for procfile in procdir:
50-
try:
51-
if procfile.isdigit():
52-
cmdfile_path = '/proc/%s/cmdline' % procfile
53-
with open(cmdfile_path, 'r') as cmdline_file:
54-
cmdline_nullseperated = cmdline_file.read()
55-
cmdline = cmdline_nullseperated.split('\0')[0:-1]
56-
if len(cmdline) is 0:
57-
continue
58-
if re.search('dhclient', cmdline[0]) is None:
59-
continue
60-
61-
i = cmdline.index('-lf')
62-
if i < len(cmdline):
63-
leasefiles.append(cmdline[i + 1])
64-
except:
65-
continue
66-
67-
return leasefiles
68-
69-
70-
def extract_identifiers_from_leasefiles(leasefilepaths):
71-
hostnames = []
72-
fixedips = []
73-
for leasefilepath in leasefilepaths:
74-
if leasefilepath is None:
75-
return None
76-
logging.debug("reading dhclient-leasefile %s", leasefilepath)
77-
with open(leasefilepath, 'r') as leasefile:
78-
leasefilecontent = leasefile.read()
79-
80-
leases = re.split("lease\s*\{([^\}]*)\}", leasefilecontent)
81-
82-
for lease in leases:
83-
if lease is None:
84-
continue
85-
if re.match("\s*\Z", lease):
86-
continue
87-
88-
match_fixedip = re.search(
89-
'fixed-address\s*(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})', lease)
90-
if match_fixedip:
91-
fixedip = match_fixedip.group(1)
92-
logging.debug("found fixed ip address in dhclient lease " +
93-
"file: %s", fixedip)
94-
if fixedip not in fixedips:
95-
fixedips.append(fixedip)
96-
97-
match_hostname = re.search(
98-
'option\s*host-name\s*"([\w\-.]+)"', lease)
99-
if match_hostname:
100-
hostname = match_hostname.group(1)
101-
logging.debug("found hostname in dhclient lease file: %s",
102-
hostname)
103-
104-
# some dhcp servers serve FQDNs, some only short hostnames.
105-
# here, we do want short hostnames.
106-
match_fqdn = re.search('([\w\-]+)\..*', hostname)
107-
if match_fqdn:
108-
hostname_fqdn = hostname
109-
hostname = match_fqdn.group(1)
110-
logging.debug(
111-
"hostname: %s fqdn: %s" % (hostname_fqdn, hostname))
112-
113-
if hostname not in hostnames:
114-
hostnames.append(hostname)
115-
116-
return (hostnames, fixedips)
117-
118-
11941
def process_ip(ip):
12042
# RHEV-tags cannot contain dots in their names.
12143
# so we replace dots by hyphens.
12244
return ip.replace('.', '-')
12345

12446

12547
def get_thinclient_identifiers():
126-
leasefilepaths = find_dhclient_leasefiles()
127-
(hostnames, fixedips) = extract_identifiers_from_leasefiles(leasefilepaths)
48+
(hostnames, fixedips) = extract_identifiers_from_nmcli()
12849
identifiers = (["thinclient-hostname-%s" % x for x in hostnames] +
12950
["thinc*ient-hostname-%s" % x for x in hostnames] +
13051
["thinclient-ip-%s" % process_ip(x) for x in fixedips] +
@@ -135,8 +56,7 @@ def get_thinclient_identifiers():
13556

13657

13758
def get_dhcp_hostnames():
138-
leasefilepaths = find_dhclient_leasefiles()
139-
(hostnames, fixedips) = extract_identifiers_from_leasefiles(leasefilepaths)
59+
hostnames, _ = extract_identifiers_from_nmcli()
14060
return hostnames
14161

14262

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
#!/usr/bin/env python
2+
# -*- coding: UTF-8 -*-
3+
# vim: autoindent expandtab tabstop=4 sw=4 sts=4 filetype=python
4+
5+
# Copyright (c) 2013-2016, Adfinis SyGroup AG
6+
#
7+
# This file is part of Virtesk VDI.
8+
#
9+
# Virtesk VDI is free software: you can redistribute it and/or modify
10+
# it under the terms of the GNU General Public License as published by
11+
# the Free Software Foundation, either version 3 of the License, or
12+
# (at your option) any later version.
13+
#
14+
# Virtesk VDI is distributed in the hope that it will be useful,
15+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
16+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17+
# GNU General Public License for more details.
18+
#
19+
# You should have received a copy of the GNU General Public License
20+
# along with Virtesk VDI. If not, see <http://www.gnu.org/licenses/>.
21+
22+
# System Imports
23+
import subprocess
24+
import re
25+
26+
27+
def extract_identifiers_from_nmcli():
28+
fixedips = []
29+
hostnames = []
30+
nmcli_output = subprocess.check_output(['nmcli', 'device', 'show'], env={'LC_ALL': 'C'})
31+
for line in nmcli_output.splitlines():
32+
if re.match('^IP4\.ADDRESS.', line):
33+
key, value = get_line_key_value(line)
34+
value = value[:value.index("/")].strip()
35+
fixedips.append(value)
36+
elif re.match('^GENERAL\.CONNECTION.', line):
37+
key, value = get_line_key_value(line)
38+
dhcp_hostname = get_dhcp_hostname_from_connection(value)
39+
if dhcp_hostname:
40+
hostnames.append(dhcp_hostname)
41+
return (hostnames, fixedips)
42+
43+
44+
def get_dhcp_hostname_from_connection(name):
45+
if name and name != '--':
46+
nmcli_output = subprocess.check_output(['nmcli', 'con', 'show', name], env={'LC_ALL': 'C'})
47+
for line in nmcli_output.splitlines():
48+
if re.match('^ipv4\.dhcp-hostname.', line):
49+
key, value = get_line_key_value(line)
50+
if value != '--':
51+
return value
52+
53+
54+
def get_line_key_value(line):
55+
if line:
56+
values = str(line).split(':')
57+
if len(values) == 2:
58+
key, value = values
59+
value = value.strip()
60+
return (key, value)
61+
62+
63+
def get_active_connections():
64+
nmcli_output = subprocess.check_output(['nmcli', '-t', '-f', 'state', 'con', 'show', '--active'], env={'LC_ALL': 'C'})
65+
return len(nmcli_output.splitlines())
66+
67+
def main():
68+
print(extract_identifiers_from_nmcli())
69+
70+
71+
if __name__ == "__main__":
72+
main()
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
import find_thinclient_identifier_nmcli as find_nmcli
2+
3+
4+
def test_extract_identifiers_from_nmcli(mock, nmcli_device_show, nmcli_con_show, nmcli_con_show_empty):
5+
def my_check_output(cmd, **kwargs):
6+
if cmd[:4] == ['nmcli', 'con', 'show', 'ADSY-EAP']:
7+
return nmcli_con_show
8+
elif cmd[:3] == ['nmcli', 'con', 'show']:
9+
return nmcli_con_show_empty
10+
elif cmd[:3] == ['nmcli', 'device', 'show']:
11+
return nmcli_device_show
12+
mock.patch('find_thinclient_identifier_nmcli.subprocess.check_output', my_check_output)
13+
return_value = find_nmcli.extract_identifiers_from_nmcli()
14+
15+
assert return_value == (['a-hostname-appeared'], ['172.17.0.1', '10.9.5.185', '127.0.0.1'])
16+
17+
18+
def test_get_dhcp_hostname_from_connection(mock, nmcli_con_show):
19+
patched_check_output = mock.patch('find_thinclient_identifier_nmcli.subprocess.check_output')
20+
patched_check_output.return_value = nmcli_con_show
21+
return_value = find_nmcli.get_dhcp_hostname_from_connection('SOME-NAME')
22+
assert return_value == 'a-hostname-appeared'
23+
24+
25+
def test_get_dhcp_hostname_from_connection_empty(mock, nmcli_con_show_empty):
26+
patched_check_output = mock.patch('find_thinclient_identifier_nmcli.subprocess.check_output')
27+
patched_check_output.return_value = nmcli_con_show_empty
28+
return_value = find_nmcli.get_dhcp_hostname_from_connection('SOME-NAME')
29+
assert return_value is None
30+
31+
32+
def test_get_line_key_value(nmcli_device_show_single_line):
33+
return_value = find_nmcli.get_line_key_value(nmcli_device_show_single_line)
34+
key, value = return_value
35+
assert key == 'IP4.ADDRESS[1]'
36+
assert value == '10.9.5.185/16'
37+
38+
39+
def test_get_line_key_value_none():
40+
return_value = find_nmcli.get_line_key_value('')
41+
assert return_value is None
42+
43+
44+
def test_get_active_connections(mock, active_connection):
45+
patched_check_output = mock.patch('find_thinclient_identifier_nmcli.subprocess.check_output')
46+
patched_check_output.return_value = active_connection
47+
return_value = find_nmcli.get_active_connections()
48+
assert return_value == 2
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
activated
2+
activated

0 commit comments

Comments
 (0)