test_network.py 36 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703
  1. # -*- coding: utf-8 -*-
  2. # Import Python libs
  3. from __future__ import absolute_import, unicode_literals, print_function
  4. import logging
  5. import socket
  6. import textwrap
  7. # Import Salt Testing libs
  8. from tests.support.unit import TestCase
  9. from tests.support.mock import (
  10. MagicMock,
  11. mock_open,
  12. create_autospec,
  13. patch,
  14. )
  15. # Import salt libs
  16. import salt.utils.network as network
  17. import salt.exceptions
  18. from salt._compat import ipaddress
  19. log = logging.getLogger(__name__)
  20. LINUX = '''\
  21. eth0 Link encap:Ethernet HWaddr e0:3f:49:85:6a:af
  22. inet addr:10.10.10.56 Bcast:10.10.10.255 Mask:255.255.252.0
  23. inet6 addr: fe80::e23f:49ff:fe85:6aaf/64 Scope:Link
  24. UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1
  25. RX packets:643363 errors:0 dropped:0 overruns:0 frame:0
  26. TX packets:196539 errors:0 dropped:0 overruns:0 carrier:0
  27. collisions:0 txqueuelen:1000
  28. RX bytes:386388355 (368.4 MiB) TX bytes:25600939 (24.4 MiB)
  29. lo Link encap:Local Loopback
  30. inet addr:127.0.0.1 Mask:255.0.0.0
  31. inet6 addr: ::1/128 Scope:Host
  32. UP LOOPBACK RUNNING MTU:65536 Metric:1
  33. RX packets:548901 errors:0 dropped:0 overruns:0 frame:0
  34. TX packets:548901 errors:0 dropped:0 overruns:0 carrier:0
  35. collisions:0 txqueuelen:0
  36. RX bytes:613479895 (585.0 MiB) TX bytes:613479895 (585.0 MiB)
  37. '''
  38. FREEBSD = '''
  39. em0: flags=8843<UP,BROADCAST,RUNNING,SIMPLEX,MULTICAST> metric 0 mtu 1500
  40. options=4219b<RXCSUM,TXCSUM,VLAN_MTU,VLAN_HWTAGGING,VLAN_HWCSUM,TSO4,WOL_MAGIC,VLAN_HWTSO>
  41. ether 00:30:48:ff:ff:ff
  42. inet 10.10.10.250 netmask 0xffffffe0 broadcast 10.10.10.255
  43. inet 10.10.10.56 netmask 0xffffffc0 broadcast 10.10.10.63
  44. media: Ethernet autoselect (1000baseT <full-duplex>)
  45. status: active
  46. em1: flags=8c02<BROADCAST,OACTIVE,SIMPLEX,MULTICAST> metric 0 mtu 1500
  47. options=4219b<RXCSUM,TXCSUM,VLAN_MTU,VLAN_HWTAGGING,VLAN_HWCSUM,TSO4,WOL_MAGIC,VLAN_HWTSO>
  48. ether 00:30:48:aa:aa:aa
  49. media: Ethernet autoselect
  50. status: no carrier
  51. plip0: flags=8810<POINTOPOINT,SIMPLEX,MULTICAST> metric 0 mtu 1500
  52. lo0: flags=8049<UP,LOOPBACK,RUNNING,MULTICAST> metric 0 mtu 16384
  53. options=3<RXCSUM,TXCSUM>
  54. inet6 fe80::1%lo0 prefixlen 64 scopeid 0x8
  55. inet6 ::1 prefixlen 128
  56. inet 127.0.0.1 netmask 0xff000000
  57. nd6 options=3<PERFORMNUD,ACCEPT_RTADV>
  58. tun0: flags=8051<UP,POINTOPOINT,RUNNING,MULTICAST> metric 0 mtu 1500
  59. options=80000<LINKSTATE>
  60. inet 10.12.0.1 --> 10.12.0.2 netmask 0xffffffff
  61. Opened by PID 1964
  62. '''
  63. SOLARIS = '''\
  64. lo0: flags=2001000849<UP,LOOPBACK,RUNNING,MULTICAST,IPv4,VIRTUAL> mtu 8232 index 1
  65. inet 127.0.0.1 netmask ff000000
  66. net0: flags=100001100943<UP,BROADCAST,RUNNING,PROMISC,MULTICAST,ROUTER,IPv4,PHYSRUNNING> mtu 1500 index 2
  67. inet 10.10.10.38 netmask ffffffe0 broadcast 10.10.10.63
  68. ilbint0: flags=110001100843<UP,BROADCAST,RUNNING,MULTICAST,ROUTER,IPv4,VRRP,PHYSRUNNING> mtu 1500 index 3
  69. inet 10.6.0.11 netmask ffffff00 broadcast 10.6.0.255
  70. ilbext0: flags=110001100843<UP,BROADCAST,RUNNING,MULTICAST,ROUTER,IPv4,VRRP,PHYSRUNNING> mtu 1500 index 4
  71. inet 10.10.11.11 netmask ffffffe0 broadcast 10.10.11.31
  72. ilbext0:1: flags=110001100843<UP,BROADCAST,RUNNING,MULTICAST,ROUTER,IPv4,VRRP,PHYSRUNNING> mtu 1500 index 4
  73. inet 10.10.11.12 netmask ffffffe0 broadcast 10.10.11.31
  74. vpn0: flags=1000011008d1<UP,POINTOPOINT,RUNNING,NOARP,MULTICAST,ROUTER,IPv4,PHYSRUNNING> mtu 1480 index 5
  75. inet tunnel src 10.10.11.12 tunnel dst 10.10.5.5
  76. tunnel hop limit 64
  77. inet 10.6.0.14 --> 10.6.0.15 netmask ff000000
  78. lo0: flags=2002000849<UP,LOOPBACK,RUNNING,MULTICAST,IPv6,VIRTUAL> mtu 8252 index 1
  79. inet6 ::1/128
  80. net0: flags=120002004941<UP,RUNNING,PROMISC,MULTICAST,DHCP,IPv6,PHYSRUNNING> mtu 1500 index 2
  81. inet6 fe80::221:9bff:fefd:2a22/10
  82. ilbint0: flags=120002000840<RUNNING,MULTICAST,IPv6,PHYSRUNNING> mtu 1500 index 3
  83. inet6 ::/0
  84. ilbext0: flags=120002000840<RUNNING,MULTICAST,IPv6,PHYSRUNNING> mtu 1500 index 4
  85. inet6 ::/0
  86. vpn0: flags=120002200850<POINTOPOINT,RUNNING,MULTICAST,NONUD,IPv6,PHYSRUNNING> mtu 1480 index 5
  87. inet tunnel src 10.10.11.12 tunnel dst 10.10.5.5
  88. tunnel hop limit 64
  89. inet6 ::/0 --> fe80::b2d6:7c10
  90. '''
  91. NETBSD = '''\
  92. vioif0: flags=0x8943<UP,BROADCAST,RUNNING,PROMISC,SIMPLEX,MULTICAST> mtu 1500
  93. ec_capabilities=1<VLAN_MTU>
  94. ec_enabled=0
  95. address: 00:a0:98:e6:83:18
  96. inet 192.168.1.80/24 broadcast 192.168.1.255 flags 0x0
  97. inet6 fe80::2a0:98ff:fee6:8318%vioif0/64 flags 0x0 scopeid 0x1
  98. lo0: flags=0x8049<UP,LOOPBACK,RUNNING,MULTICAST> mtu 33624
  99. inet 127.0.0.1/8 flags 0x0
  100. inet6 ::1/128 flags 0x20<NODAD>
  101. inet6 fe80::1%lo0/64 flags 0x0 scopeid 0x2
  102. '''
  103. FREEBSD_SOCKSTAT = '''\
  104. USER COMMAND PID FD PROTO LOCAL ADDRESS FOREIGN ADDRESS
  105. root python2.7 1294 41 tcp4 127.0.0.1:61115 127.0.0.1:4506
  106. '''
  107. FREEBSD_SOCKSTAT_WITH_FAT_PID = '''\
  108. USER COMMAND PID FD PROTO LOCAL ADDRESS FOREIGN ADDRESS
  109. salt-master python2.781106 35 tcp4 127.0.0.1:61115 127.0.0.1:4506
  110. '''
  111. NETLINK_SS = '''
  112. State Recv-Q Send-Q Local Address:Port Peer Address:Port
  113. ESTAB 0 0 127.0.0.1:56726 127.0.0.1:4505
  114. ESTAB 0 0 ::ffff:1.2.3.4:5678 ::ffff:1.2.3.4:4505
  115. '''
  116. LINUX_NETLINK_SS_OUTPUT = '''\
  117. State Recv-Q Send-Q Local Address:Port Peer Address:Port
  118. TIME-WAIT 0 0 [::1]:8009 [::1]:40368
  119. LISTEN 0 128 127.0.0.1:5903 0.0.0.0:*
  120. ESTAB 0 0 [::ffff:127.0.0.1]:4506 [::ffff:127.0.0.1]:32315
  121. ESTAB 0 0 192.168.122.1:4506 192.168.122.177:24545
  122. '''
  123. IPV4_SUBNETS = {True: ('10.10.0.0/24',),
  124. False: ('10.10.0.0', '10.10.0.0/33', 'FOO', 9, '0.9.800.1000/24')}
  125. IPV6_SUBNETS = {True: ('::1/128',),
  126. False: ('::1', '::1/129', 'FOO', 9, 'aj01::feac/64')}
  127. class NetworkTestCase(TestCase):
  128. def test_sanitize_host(self):
  129. ret = network.sanitize_host('10.1./2.$3')
  130. self.assertEqual(ret, '10.1.2.3')
  131. def test_host_to_ips(self):
  132. '''
  133. NOTE: When this test fails it's usually because the IP address has
  134. changed. In these cases, we just need to update the IP address in the
  135. assertion.
  136. '''
  137. def _side_effect(host, *args):
  138. try:
  139. return {
  140. 'github.com': [
  141. (2, 1, 6, '', ('192.30.255.112', 0)),
  142. (2, 1, 6, '', ('192.30.255.113', 0)),
  143. ],
  144. 'ipv6host.foo': [
  145. (socket.AF_INET6, 1, 6, '', ('2001:a71::1', 0, 0, 0)),
  146. ],
  147. }[host]
  148. except KeyError:
  149. raise socket.gaierror(-2, 'Name or service not known')
  150. getaddrinfo_mock = MagicMock(side_effect=_side_effect)
  151. with patch.object(socket, 'getaddrinfo', getaddrinfo_mock):
  152. # Test host that can be resolved
  153. ret = network.host_to_ips('github.com')
  154. self.assertEqual(ret, ['192.30.255.112', '192.30.255.113'])
  155. # Test ipv6
  156. ret = network.host_to_ips('ipv6host.foo')
  157. self.assertEqual(ret, ['2001:a71::1'])
  158. # Test host that can't be resolved
  159. ret = network.host_to_ips('someothersite.com')
  160. self.assertEqual(ret, None)
  161. def test_generate_minion_id(self):
  162. self.assertTrue(network.generate_minion_id())
  163. def test__generate_minion_id_with_unicode_in_etc_hosts(self):
  164. '''
  165. Test that unicode in /etc/hosts doesn't raise an error when
  166. _generate_minion_id() helper is called to gather the hosts.
  167. '''
  168. content = textwrap.dedent('''\
  169. # 以下为主机名解析
  170. ## ccc
  171. 127.0.0.1 localhost thisismyhostname # 本机
  172. ''')
  173. fopen_mock = mock_open(read_data={'/etc/hosts': content})
  174. with patch('salt.utils.files.fopen', fopen_mock):
  175. assert 'thisismyhostname' in network._generate_minion_id()
  176. def test_is_ip(self):
  177. self.assertTrue(network.is_ip('10.10.0.3'))
  178. self.assertFalse(network.is_ip('0.9.800.1000'))
  179. # Check 16-char-long unicode string
  180. # https://github.com/saltstack/salt/issues/51258
  181. self.assertFalse(network.is_ipv6('sixteen-char-str'))
  182. def test_is_ipv4(self):
  183. self.assertTrue(network.is_ipv4('10.10.0.3'))
  184. self.assertFalse(network.is_ipv4('10.100.1'))
  185. self.assertFalse(network.is_ipv4('2001:db8:0:1:1:1:1:1'))
  186. # Check 16-char-long unicode string
  187. # https://github.com/saltstack/salt/issues/51258
  188. self.assertFalse(network.is_ipv4('sixteen-char-str'))
  189. def test_is_ipv6(self):
  190. self.assertTrue(network.is_ipv6('2001:db8:0:1:1:1:1:1'))
  191. self.assertTrue(network.is_ipv6('0:0:0:0:0:0:0:1'))
  192. self.assertTrue(network.is_ipv6('::1'))
  193. self.assertTrue(network.is_ipv6('::'))
  194. self.assertTrue(network.is_ipv6('2001:0db8:85a3:0000:0000:8a2e:0370:7334'))
  195. self.assertTrue(network.is_ipv6('2001:0db8:85a3::8a2e:0370:7334'))
  196. self.assertFalse(network.is_ipv6('2001:0db8:0370:7334'))
  197. self.assertFalse(network.is_ipv6('2001:0db8:::0370:7334'))
  198. self.assertFalse(network.is_ipv6('10.0.1.2'))
  199. self.assertFalse(network.is_ipv6('2001.0db8.85a3.0000.0000.8a2e.0370.7334'))
  200. # Check 16-char-long unicode string
  201. # https://github.com/saltstack/salt/issues/51258
  202. self.assertFalse(network.is_ipv6('sixteen-char-str'))
  203. def test_ipv6(self):
  204. self.assertTrue(network.ipv6('2001:db8:0:1:1:1:1:1'))
  205. self.assertTrue(network.ipv6('0:0:0:0:0:0:0:1'))
  206. self.assertTrue(network.ipv6('::1'))
  207. self.assertTrue(network.ipv6('::'))
  208. self.assertTrue(network.ipv6('2001:0db8:85a3:0000:0000:8a2e:0370:7334'))
  209. self.assertTrue(network.ipv6('2001:0db8:85a3::8a2e:0370:7334'))
  210. self.assertTrue(network.ipv6('2001:67c:2e8::/48'))
  211. def test_parse_host_port(self):
  212. _ip = ipaddress.ip_address
  213. good_host_ports = {
  214. '10.10.0.3': (_ip('10.10.0.3').compressed, None),
  215. '10.10.0.3:1234': (_ip('10.10.0.3').compressed, 1234),
  216. '2001:0db8:85a3::8a2e:0370:7334': (_ip('2001:0db8:85a3::8a2e:0370:7334').compressed, None),
  217. '[2001:0db8:85a3::8a2e:0370:7334]:1234': (_ip('2001:0db8:85a3::8a2e:0370:7334').compressed, 1234),
  218. '2001:0db8:85a3::7334': (_ip('2001:0db8:85a3::7334').compressed, None),
  219. '[2001:0db8:85a3::7334]:1234': (_ip('2001:0db8:85a3::7334').compressed, 1234)
  220. }
  221. bad_host_ports = [
  222. '10.10.0.3/24',
  223. '10.10.0.3::1234',
  224. '2001:0db8:0370:7334',
  225. '2001:0db8:0370::7334]:1234',
  226. '2001:0db8:0370:0:a:b:c:d:1234'
  227. ]
  228. for host_port, assertion_value in good_host_ports.items():
  229. host = port = None
  230. host, port = network.parse_host_port(host_port)
  231. self.assertEqual((host, port), assertion_value)
  232. for host_port in bad_host_ports:
  233. try:
  234. self.assertRaises(ValueError, network.parse_host_port, host_port)
  235. except AssertionError as _e_:
  236. log.error('bad host_port value: "%s" failed to trigger ValueError exception', host_port)
  237. raise _e_
  238. def test_dns_check(self):
  239. hosts = [
  240. {'host': '10.10.0.3',
  241. 'port': '',
  242. 'mocked': [(2, 1, 6, '', ('10.10.0.3', 0))],
  243. 'ret': '10.10.0.3'},
  244. {'host': '10.10.0.3',
  245. 'port': '1234',
  246. 'mocked': [(2, 1, 6, '', ('10.10.0.3', 0))],
  247. 'ret': '10.10.0.3'},
  248. {'host': '2001:0db8:85a3::8a2e:0370:7334',
  249. 'port': '',
  250. 'mocked': [(10, 1, 6, '', ('2001:db8:85a3::8a2e:370:7334', 0, 0, 0))],
  251. 'ret': '[2001:db8:85a3::8a2e:370:7334]'},
  252. {'host': '2001:0db8:85a3::8a2e:370:7334',
  253. 'port': '1234',
  254. 'mocked': [(10, 1, 6, '', ('2001:db8:85a3::8a2e:370:7334', 0, 0, 0))],
  255. 'ret': '[2001:db8:85a3::8a2e:370:7334]'},
  256. {'host': 'salt-master',
  257. 'port': '1234',
  258. 'mocked': [(2, 1, 6, '', ('127.0.0.1', 0))],
  259. 'ret': '127.0.0.1'},
  260. ]
  261. for host in hosts:
  262. with patch.object(socket, 'getaddrinfo', create_autospec(socket.getaddrinfo, return_value=host['mocked'])):
  263. with patch('socket.socket', create_autospec(socket.socket)):
  264. ret = network.dns_check(host['host'], host['port'])
  265. self.assertEqual(ret, host['ret'])
  266. def test_dns_check_ipv6_filter(self):
  267. # raise exception to skip everything after the getaddrinfo call
  268. with patch.object(socket, 'getaddrinfo',
  269. create_autospec(socket.getaddrinfo, side_effect=Exception)) as getaddrinfo:
  270. for ipv6, param in [
  271. (None, socket.AF_UNSPEC),
  272. (True, socket.AF_INET6),
  273. (False, socket.AF_INET),
  274. ]:
  275. with self.assertRaises(Exception):
  276. network.dns_check('foo', '1', ipv6=ipv6)
  277. getaddrinfo.assert_called_with('foo', '1', param, socket.SOCK_STREAM)
  278. def test_dns_check_errors(self):
  279. with patch.object(socket, 'getaddrinfo', create_autospec(socket.getaddrinfo, return_value=[])):
  280. with self.assertRaisesRegex(salt.exceptions.SaltSystemExit,
  281. "DNS lookup or connection check of 'foo' failed"):
  282. network.dns_check('foo', '1')
  283. with patch.object(socket, 'getaddrinfo', create_autospec(socket.getaddrinfo, side_effect=TypeError)):
  284. with self.assertRaisesRegex(salt.exceptions.SaltSystemExit,
  285. "Invalid or unresolveable address"):
  286. network.dns_check('foo', '1')
  287. def test_test_addrs(self):
  288. # subset of real data from getaddrinfo against saltstack.com
  289. addrinfo = [(30, 2, 17, '', ('2600:9000:21eb:a800:8:1031:abc0:93a1', 0, 0, 0)),
  290. (30, 1, 6, '', ('2600:9000:21eb:a800:8:1031:abc0:93a1', 0, 0, 0)),
  291. (30, 2, 17, '', ('2600:9000:21eb:b400:8:1031:abc0:93a1', 0, 0, 0)),
  292. (30, 1, 6, '', ('2600:9000:21eb:b400:8:1031:abc0:93a1', 0, 0, 0)),
  293. (2, 1, 6, '', ('13.35.99.52', 0)), (2, 2, 17, '', ('13.35.99.85', 0)),
  294. (2, 1, 6, '', ('13.35.99.85', 0)), (2, 2, 17, '', ('13.35.99.122', 0))]
  295. with patch('socket.socket', create_autospec(socket.socket)) as s:
  296. # we connect to the first address
  297. addrs = network._test_addrs(addrinfo, 80)
  298. self.assertTrue(len(addrs) == 1)
  299. self.assertTrue(addrs[0] == addrinfo[0][4][0])
  300. # the first lookup fails, succeeds on next check
  301. s.side_effect = [socket.error, MagicMock()]
  302. addrs = network._test_addrs(addrinfo, 80)
  303. self.assertTrue(len(addrs) == 1)
  304. self.assertTrue(addrs[0] == addrinfo[2][4][0])
  305. # nothing can connect, but we've eliminated duplicates
  306. s.side_effect = socket.error
  307. addrs = network._test_addrs(addrinfo, 80)
  308. self.assertTrue(len(addrs) == 5)
  309. def test_is_subnet(self):
  310. for subnet_data in (IPV4_SUBNETS, IPV6_SUBNETS):
  311. for item in subnet_data[True]:
  312. log.debug('Testing that %s is a valid subnet', item)
  313. self.assertTrue(network.is_subnet(item))
  314. for item in subnet_data[False]:
  315. log.debug('Testing that %s is not a valid subnet', item)
  316. self.assertFalse(network.is_subnet(item))
  317. def test_is_ipv4_subnet(self):
  318. for item in IPV4_SUBNETS[True]:
  319. log.debug('Testing that %s is a valid subnet', item)
  320. self.assertTrue(network.is_ipv4_subnet(item))
  321. for item in IPV4_SUBNETS[False]:
  322. log.debug('Testing that %s is not a valid subnet', item)
  323. self.assertFalse(network.is_ipv4_subnet(item))
  324. def test_is_ipv6_subnet(self):
  325. for item in IPV6_SUBNETS[True]:
  326. log.debug('Testing that %s is a valid subnet', item)
  327. self.assertTrue(network.is_ipv6_subnet(item))
  328. for item in IPV6_SUBNETS[False]:
  329. log.debug('Testing that %s is not a valid subnet', item)
  330. self.assertFalse(network.is_ipv6_subnet(item))
  331. def test_cidr_to_ipv4_netmask(self):
  332. self.assertEqual(network.cidr_to_ipv4_netmask(24), '255.255.255.0')
  333. self.assertEqual(network.cidr_to_ipv4_netmask(21), '255.255.248.0')
  334. self.assertEqual(network.cidr_to_ipv4_netmask(17), '255.255.128.0')
  335. self.assertEqual(network.cidr_to_ipv4_netmask(9), '255.128.0.0')
  336. self.assertEqual(network.cidr_to_ipv4_netmask(36), '')
  337. self.assertEqual(network.cidr_to_ipv4_netmask('lol'), '')
  338. def test_number_of_set_bits_to_ipv4_netmask(self):
  339. set_bits_to_netmask = network._number_of_set_bits_to_ipv4_netmask(0xffffff00)
  340. self.assertEqual(set_bits_to_netmask, '255.255.255.0')
  341. set_bits_to_netmask = network._number_of_set_bits_to_ipv4_netmask(0xffff6400)
  342. def test_hex2ip(self):
  343. self.assertEqual(network.hex2ip('0x4A7D2B63'), '74.125.43.99')
  344. self.assertEqual(network.hex2ip('0x4A7D2B63', invert=True), '99.43.125.74')
  345. self.assertEqual(network.hex2ip('00000000000000000000FFFF7F000001'), '127.0.0.1')
  346. self.assertEqual(network.hex2ip('0000000000000000FFFF00000100007F', invert=True), '127.0.0.1')
  347. self.assertEqual(network.hex2ip('20010DB8000000000000000000000000'), '2001:db8::')
  348. self.assertEqual(network.hex2ip('B80D0120000000000000000000000000', invert=True), '2001:db8::')
  349. def test_interfaces_ifconfig_linux(self):
  350. interfaces = network._interfaces_ifconfig(LINUX)
  351. self.assertEqual(interfaces,
  352. {'eth0': {'hwaddr': 'e0:3f:49:85:6a:af',
  353. 'inet': [{'address': '10.10.10.56',
  354. 'broadcast': '10.10.10.255',
  355. 'netmask': '255.255.252.0'}],
  356. 'inet6': [{'address': 'fe80::e23f:49ff:fe85:6aaf',
  357. 'prefixlen': '64',
  358. 'scope': 'link'}],
  359. 'up': True},
  360. 'lo': {'inet': [{'address': '127.0.0.1',
  361. 'netmask': '255.0.0.0'}],
  362. 'inet6': [{'address': '::1',
  363. 'prefixlen': '128',
  364. 'scope': 'host'}],
  365. 'up': True}}
  366. )
  367. def test_interfaces_ifconfig_freebsd(self):
  368. interfaces = network._interfaces_ifconfig(FREEBSD)
  369. self.assertEqual(interfaces,
  370. {'': {'up': False},
  371. 'em0': {'hwaddr': '00:30:48:ff:ff:ff',
  372. 'inet': [{'address': '10.10.10.250',
  373. 'broadcast': '10.10.10.255',
  374. 'netmask': '255.255.255.224'},
  375. {'address': '10.10.10.56',
  376. 'broadcast': '10.10.10.63',
  377. 'netmask': '255.255.255.192'}],
  378. 'up': True},
  379. 'em1': {'hwaddr': '00:30:48:aa:aa:aa',
  380. 'up': False},
  381. 'lo0': {'inet': [{'address': '127.0.0.1',
  382. 'netmask': '255.0.0.0'}],
  383. 'inet6': [{'address': 'fe80::1',
  384. 'prefixlen': '64',
  385. 'scope': '0x8'},
  386. {'address': '::1',
  387. 'prefixlen': '128',
  388. 'scope': None}],
  389. 'up': True},
  390. 'plip0': {'up': False},
  391. 'tun0': {'inet': [{'address': '10.12.0.1',
  392. 'netmask': '255.255.255.255'}],
  393. 'up': True}}
  394. )
  395. def test_interfaces_ifconfig_solaris(self):
  396. with patch('salt.utils.platform.is_sunos', lambda: True):
  397. interfaces = network._interfaces_ifconfig(SOLARIS)
  398. expected_interfaces = {'ilbint0':
  399. {'inet6': [],
  400. 'inet': [{'broadcast': '10.6.0.255',
  401. 'netmask': '255.255.255.0',
  402. 'address': '10.6.0.11'}],
  403. 'up': True},
  404. 'lo0':
  405. {'inet6': [{'prefixlen': '128',
  406. 'address': '::1'}],
  407. 'inet': [{'netmask': '255.0.0.0',
  408. 'address': '127.0.0.1'}],
  409. 'up': True},
  410. 'ilbext0': {'inet6': [],
  411. 'inet': [{'broadcast': '10.10.11.31',
  412. 'netmask': '255.255.255.224',
  413. 'address': '10.10.11.11'},
  414. {'broadcast': '10.10.11.31',
  415. 'netmask': '255.255.255.224',
  416. 'address': '10.10.11.12'}],
  417. 'up': True},
  418. 'vpn0': {'inet6': [],
  419. 'inet': [{'netmask': '255.0.0.0',
  420. 'address': '10.6.0.14'}],
  421. 'up': True},
  422. 'net0': {'inet6': [{'prefixlen': '10',
  423. 'address': 'fe80::221:9bff:fefd:2a22'}],
  424. 'inet': [{'broadcast': '10.10.10.63',
  425. 'netmask': '255.255.255.224',
  426. 'address': '10.10.10.38'}],
  427. 'up': True}}
  428. self.assertEqual(interfaces, expected_interfaces)
  429. def test_interfaces_ifconfig_netbsd(self):
  430. interfaces = network._netbsd_interfaces_ifconfig(NETBSD)
  431. self.assertEqual(interfaces,
  432. {'lo0': {'inet': [{'address': '127.0.0.1', 'netmask': '255.0.0.0'}],
  433. 'inet6': [{'address': 'fe80::1',
  434. 'prefixlen': '64',
  435. 'scope': 'lo0'}],
  436. 'up': True},
  437. 'vioif0': {'hwaddr': '00:a0:98:e6:83:18',
  438. 'inet': [{'address': '192.168.1.80',
  439. 'broadcast': '192.168.1.255',
  440. 'netmask': '255.255.255.0'}],
  441. 'inet6': [{'address': 'fe80::2a0:98ff:fee6:8318',
  442. 'prefixlen': '64',
  443. 'scope': 'vioif0'}],
  444. 'up': True}}
  445. )
  446. def test_freebsd_remotes_on(self):
  447. with patch('salt.utils.platform.is_sunos', lambda: False):
  448. with patch('salt.utils.platform.is_freebsd', lambda: True):
  449. with patch('subprocess.check_output',
  450. return_value=FREEBSD_SOCKSTAT):
  451. remotes = network._freebsd_remotes_on('4506', 'remote')
  452. self.assertEqual(remotes, set(['127.0.0.1']))
  453. def test_freebsd_remotes_on_with_fat_pid(self):
  454. with patch('salt.utils.platform.is_sunos', lambda: False):
  455. with patch('salt.utils.platform.is_freebsd', lambda: True):
  456. with patch('subprocess.check_output',
  457. return_value=FREEBSD_SOCKSTAT_WITH_FAT_PID):
  458. remotes = network._freebsd_remotes_on('4506', 'remote')
  459. self.assertEqual(remotes, set(['127.0.0.1']))
  460. def test_netlink_tool_remote_on_a(self):
  461. with patch('salt.utils.platform.is_sunos', lambda: False):
  462. with patch('salt.utils.platform.is_linux', lambda: True):
  463. with patch('subprocess.check_output',
  464. return_value=LINUX_NETLINK_SS_OUTPUT):
  465. remotes = network._netlink_tool_remote_on('4506', 'local')
  466. self.assertEqual(remotes, set(['192.168.122.177', '::ffff:127.0.0.1']))
  467. def test_netlink_tool_remote_on_b(self):
  468. with patch('subprocess.check_output', return_value=NETLINK_SS):
  469. remotes = network._netlink_tool_remote_on('4505', 'remote_port')
  470. self.assertEqual(remotes, set(['127.0.0.1', '::ffff:1.2.3.4']))
  471. def test_generate_minion_id_distinct(self):
  472. '''
  473. Test if minion IDs are distinct in the pool.
  474. :return:
  475. '''
  476. with patch('platform.node', MagicMock(return_value='nodename')), \
  477. patch('socket.gethostname', MagicMock(return_value='hostname')), \
  478. patch('socket.getfqdn', MagicMock(return_value='hostname.domainname.blank')), \
  479. patch('socket.getaddrinfo', MagicMock(return_value=[(2, 3, 0, 'attrname', ('127.0.1.1', 0))])), \
  480. patch('salt.utils.files.fopen', mock_open()), \
  481. patch('salt.utils.network.ip_addrs', MagicMock(return_value=['1.2.3.4', '5.6.7.8'])):
  482. self.assertEqual(network._generate_minion_id(),
  483. ['hostname.domainname.blank', 'nodename', 'hostname', '1.2.3.4', '5.6.7.8'])
  484. def test_generate_minion_id_127_name(self):
  485. '''
  486. Test if minion IDs can be named 127.foo
  487. :return:
  488. '''
  489. with patch('platform.node', MagicMock(return_value='127')), \
  490. patch('socket.gethostname', MagicMock(return_value='127')), \
  491. patch('socket.getfqdn', MagicMock(return_value='127.domainname.blank')), \
  492. patch('socket.getaddrinfo', MagicMock(return_value=[(2, 3, 0, 'attrname', ('127.0.1.1', 0))])), \
  493. patch('salt.utils.files.fopen', mock_open()), \
  494. patch('salt.utils.network.ip_addrs', MagicMock(return_value=['1.2.3.4', '5.6.7.8'])):
  495. self.assertEqual(network._generate_minion_id(),
  496. ['127.domainname.blank', '127', '1.2.3.4', '5.6.7.8'])
  497. def test_generate_minion_id_127_name_startswith(self):
  498. '''
  499. Test if minion IDs can be named starting from "127"
  500. :return:
  501. '''
  502. with patch('platform.node', MagicMock(return_value='127890')), \
  503. patch('socket.gethostname', MagicMock(return_value='127890')), \
  504. patch('socket.getfqdn', MagicMock(return_value='127890.domainname.blank')), \
  505. patch('socket.getaddrinfo', MagicMock(return_value=[(2, 3, 0, 'attrname', ('127.0.1.1', 0))])), \
  506. patch('salt.utils.files.fopen', mock_open()), \
  507. patch('salt.utils.network.ip_addrs', MagicMock(return_value=['1.2.3.4', '5.6.7.8'])):
  508. self.assertEqual(network._generate_minion_id(),
  509. ['127890.domainname.blank', '127890', '1.2.3.4', '5.6.7.8'])
  510. def test_generate_minion_id_duplicate(self):
  511. '''
  512. Test if IP addresses in the minion IDs are distinct in the pool
  513. :return:
  514. '''
  515. with patch('platform.node', MagicMock(return_value='hostname')), \
  516. patch('socket.gethostname', MagicMock(return_value='hostname')), \
  517. patch('socket.getfqdn', MagicMock(return_value='hostname')), \
  518. patch('socket.getaddrinfo', MagicMock(return_value=[(2, 3, 0, 'hostname', ('127.0.1.1', 0))])), \
  519. patch('salt.utils.files.fopen', mock_open()), \
  520. patch('salt.utils.network.ip_addrs', MagicMock(return_value=['1.2.3.4', '1.2.3.4', '1.2.3.4'])):
  521. self.assertEqual(network._generate_minion_id(), ['hostname', '1.2.3.4'])
  522. def test_generate_minion_id_platform_used(self):
  523. '''
  524. Test if platform.node is used for the first occurrence.
  525. The platform.node is most common hostname resolver before anything else.
  526. :return:
  527. '''
  528. with patch('platform.node', MagicMock(return_value='very.long.and.complex.domain.name')), \
  529. patch('socket.gethostname', MagicMock(return_value='hostname')), \
  530. patch('socket.getfqdn', MagicMock(return_value='')), \
  531. patch('socket.getaddrinfo', MagicMock(return_value=[(2, 3, 0, 'hostname', ('127.0.1.1', 0))])), \
  532. patch('salt.utils.files.fopen', mock_open()), \
  533. patch('salt.utils.network.ip_addrs', MagicMock(return_value=['1.2.3.4', '1.2.3.4', '1.2.3.4'])):
  534. self.assertEqual(network.generate_minion_id(), 'very.long.and.complex.domain.name')
  535. def test_generate_minion_id_platform_localhost_filtered(self):
  536. '''
  537. Test if localhost is filtered from the first occurrence.
  538. :return:
  539. '''
  540. with patch('platform.node', MagicMock(return_value='localhost')), \
  541. patch('socket.gethostname', MagicMock(return_value='pick.me')), \
  542. patch('socket.getfqdn', MagicMock(return_value='hostname.domainname.blank')), \
  543. patch('socket.getaddrinfo', MagicMock(return_value=[(2, 3, 0, 'hostname', ('127.0.1.1', 0))])), \
  544. patch('salt.utils.files.fopen', mock_open()), \
  545. patch('salt.utils.network.ip_addrs', MagicMock(return_value=['1.2.3.4', '1.2.3.4', '1.2.3.4'])):
  546. self.assertEqual(network.generate_minion_id(), 'hostname.domainname.blank')
  547. def test_generate_minion_id_platform_localhost_filtered_all(self):
  548. '''
  549. Test if any of the localhost is filtered from everywhere.
  550. :return:
  551. '''
  552. with patch('platform.node', MagicMock(return_value='localhost')), \
  553. patch('socket.gethostname', MagicMock(return_value='ip6-loopback')), \
  554. patch('socket.getfqdn', MagicMock(return_value='ip6-localhost')), \
  555. patch('socket.getaddrinfo', MagicMock(return_value=[(2, 3, 0, 'localhost', ('127.0.1.1', 0))])), \
  556. patch('salt.utils.files.fopen', mock_open()), \
  557. patch('salt.utils.network.ip_addrs', MagicMock(return_value=['127.0.0.1', '::1', 'fe00::0', 'fe02::1', '1.2.3.4'])):
  558. self.assertEqual(network.generate_minion_id(), '1.2.3.4')
  559. def test_generate_minion_id_platform_localhost_only(self):
  560. '''
  561. Test if there is no other choice but localhost.
  562. :return:
  563. '''
  564. with patch('platform.node', MagicMock(return_value='localhost')), \
  565. patch('socket.gethostname', MagicMock(return_value='ip6-loopback')), \
  566. patch('socket.getfqdn', MagicMock(return_value='ip6-localhost')), \
  567. patch('socket.getaddrinfo', MagicMock(return_value=[(2, 3, 0, 'localhost', ('127.0.1.1', 0))])), \
  568. patch('salt.utils.files.fopen', mock_open()), \
  569. patch('salt.utils.network.ip_addrs', MagicMock(return_value=['127.0.0.1', '::1', 'fe00::0', 'fe02::1'])):
  570. self.assertEqual(network.generate_minion_id(), 'localhost')
  571. def test_generate_minion_id_platform_fqdn(self):
  572. '''
  573. Test if fqdn is picked up.
  574. :return:
  575. '''
  576. with patch('platform.node', MagicMock(return_value='localhost')), \
  577. patch('socket.gethostname', MagicMock(return_value='ip6-loopback')), \
  578. patch('socket.getfqdn', MagicMock(return_value='pick.me')), \
  579. patch('socket.getaddrinfo', MagicMock(return_value=[(2, 3, 0, 'localhost', ('127.0.1.1', 0))])), \
  580. patch('salt.utils.files.fopen', mock_open()), \
  581. patch('salt.utils.network.ip_addrs', MagicMock(return_value=['127.0.0.1', '::1', 'fe00::0', 'fe02::1'])):
  582. self.assertEqual(network.generate_minion_id(), 'pick.me')
  583. def test_generate_minion_id_platform_localhost_addrinfo(self):
  584. '''
  585. Test if addinfo is picked up.
  586. :return:
  587. '''
  588. with patch('platform.node', MagicMock(return_value='localhost')), \
  589. patch('socket.gethostname', MagicMock(return_value='ip6-loopback')), \
  590. patch('socket.getfqdn', MagicMock(return_value='ip6-localhost')), \
  591. patch('socket.getaddrinfo', MagicMock(return_value=[(2, 3, 0, 'pick.me', ('127.0.1.1', 0))])), \
  592. patch('salt.utils.files.fopen', mock_open()), \
  593. patch('salt.utils.network.ip_addrs', MagicMock(return_value=['127.0.0.1', '::1', 'fe00::0', 'fe02::1'])):
  594. self.assertEqual(network.generate_minion_id(), 'pick.me')
  595. def test_generate_minion_id_platform_ip_addr_only(self):
  596. '''
  597. Test if IP address is the only what is used as a Minion ID in case no DNS name.
  598. :return:
  599. '''
  600. with patch('platform.node', MagicMock(return_value='localhost')), \
  601. patch('socket.gethostname', MagicMock(return_value='ip6-loopback')), \
  602. patch('socket.getfqdn', MagicMock(return_value='ip6-localhost')), \
  603. patch('socket.getaddrinfo', MagicMock(return_value=[(2, 3, 0, 'localhost', ('127.0.1.1', 0))])), \
  604. patch('salt.utils.files.fopen', mock_open()), \
  605. patch('salt.utils.network.ip_addrs', MagicMock(return_value=['127.0.0.1', '::1', 'fe00::0', 'fe02::1', '1.2.3.4'])):
  606. self.assertEqual(network.generate_minion_id(), '1.2.3.4')
  607. def test_gen_mac(self):
  608. with patch('random.randint', return_value=1) as random_mock:
  609. self.assertEqual(random_mock.return_value, 1)
  610. ret = network.gen_mac('00:16:3E')
  611. expected_mac = '00:16:3E:01:01:01'
  612. self.assertEqual(ret, expected_mac)
  613. def test_mac_str_to_bytes(self):
  614. self.assertRaises(ValueError, network.mac_str_to_bytes, '31337')
  615. self.assertRaises(ValueError, network.mac_str_to_bytes, '0001020304056')
  616. self.assertRaises(ValueError, network.mac_str_to_bytes, '00:01:02:03:04:056')
  617. self.assertRaises(ValueError, network.mac_str_to_bytes, 'a0:b0:c0:d0:e0:fg')
  618. self.assertEqual(b'\x10\x08\x06\x04\x02\x00', network.mac_str_to_bytes('100806040200'))
  619. self.assertEqual(b'\xf8\xe7\xd6\xc5\xb4\xa3', network.mac_str_to_bytes('f8e7d6c5b4a3'))
  620. def test_generate_minion_id_with_long_hostname(self):
  621. '''
  622. Validate the fix for:
  623. https://github.com/saltstack/salt/issues/51160
  624. '''
  625. long_name = 'localhost-abcdefghijklmnopqrstuvwxyz-abcdefghijklmnopqrstuvwxyz'
  626. with patch('socket.gethostname', MagicMock(return_value=long_name)):
  627. # An exception is raised if unicode is passed to socket.getfqdn
  628. minion_id = network.generate_minion_id()
  629. assert minion_id != '', minion_id