cloudstack/test/integration/smoke/test_network_ipv6.py
Wei Zhou 87284f03f0
Upgrade to JRE17 and Upgrade System VMs/VRs to Python3 and Debian 12 (#8497)
* Update to 4.20.0

* Update to python3

* Upgrade to JRE 17

* Upgrade to Debian 12.4.0

* VR: upgrade to python3

for f in `find systemvm/ -name *.py`;do
    if grep "print " $f >/dev/null;then
        2to3-2.7 -w $f
    else
        2to3-2.7 -p -w $f
    fi
done

* java: Use JRE17 in cloudstack packages and systemvmtemplate

Signed-off-by: Rohit Yadav <rohit.yadav@shapeblue.com>

* Add --add-opens to JAVA_OPTS in systemd config

* Add --add-opens to JAVA_OPTS in systemd config for usage

* python3: fix "TypeError: a bytes-like object is required, not 'str'"

* python3: fix "ValueError: must have exactly one of create/read/write/append mode"

* Add --add-exports=java.base/sun.security.x509=ALL-UNNAMED for management server

* Use pip3 instead of pip for centos8

* python3: fix "TypeError: write() argument must be str, not bytes"

```
root@r-1037-VM:~# /opt/cloud/bin/passwd_server_ip.py 10.1.1.1
Traceback (most recent call last):
  File "/opt/cloud/bin/passwd_server_ip.py", line 201, in <module>
    serve()
  File "/opt/cloud/bin/passwd_server_ip.py", line 187, in serve
    initToken()
  File "/opt/cloud/bin/passwd_server_ip.py", line 60, in initToken
    f.write(secureToken)
TypeError: write() argument must be str, not bytes
root@r-1037-VM:~#
```

* Python3: fix "name 'file' is not defined"

```
root@r-1037-VM:~# /opt/cloud/bin/passwd_server_ip.py 10.1.1.1
Traceback (most recent call last):
  File "/opt/cloud/bin/passwd_server_ip.py", line 201, in <module>
    serve()
  File "/opt/cloud/bin/passwd_server_ip.py", line 188, in serve
    loadPasswordFile()
  File "/opt/cloud/bin/passwd_server_ip.py", line 67, in loadPasswordFile
    with file(getPasswordFile()) as f:
NameError: name 'file' is not defined
```

* python3: fix "TypeError: write() argument must be str, not bytes" (two more files)

* Upgrade jaxb version

* python3: fix more "TypeError: a bytes-like object is required, not str"

* python3: fix "Failed to update password server"

Failed to update password server due to: POST data should be bytes, an iterable of bytes, or a file object. It cannot be of type str.

* python3: fix "bad duration value: ikelifetime=24.0h"

Jan 15 13:57:20 systemvm ipsec[3080]: # bad duration value: ikelifetime=24.0h

* python3: fix password server "invalid save_password token"

* test: incease retries in test_vpc_vpn.py

* python3: fix passwd_server_ip.py

see error below
```
Jan 15 18:51:21 systemvm passwd_server_ip.py[1507]: ----------------------------------------
Jan 15 18:51:21 systemvm passwd_server_ip.py[1507]: Exception occurred during processing of request from ('10.1.1.129', 32782)
Jan 15 18:51:21 systemvm passwd_server_ip.py[1507]: Traceback (most recent call last):
Jan 15 18:51:21 systemvm passwd_server_ip.py[1507]:   File "/usr/lib/python3.9/socketserver.py", line 650, in process_request_thread
Jan 15 18:51:21 systemvm passwd_server_ip.py[1507]:     self.finish_request(request, client_address)
Jan 15 18:51:21 systemvm passwd_server_ip.py[1507]:   File "/usr/lib/python3.9/socketserver.py", line 360, in finish_request
Jan 15 18:51:21 systemvm passwd_server_ip.py[1507]:     self.RequestHandlerClass(request, client_address, self)
Jan 15 18:51:21 systemvm passwd_server_ip.py[1507]:   File "/usr/lib/python3.9/socketserver.py", line 720, in __init__
Jan 15 18:51:21 systemvm passwd_server_ip.py[1507]:     self.handle()
Jan 15 18:51:21 systemvm passwd_server_ip.py[1507]:   File "/usr/lib/python3.9/http/server.py", line 427, in handle
Jan 15 18:51:21 systemvm passwd_server_ip.py[1507]:     self.handle_one_request()
Jan 15 18:51:21 systemvm passwd_server_ip.py[1507]:   File "/usr/lib/python3.9/http/server.py", line 415, in handle_one_request
Jan 15 18:51:21 systemvm passwd_server_ip.py[1507]:     method()
Jan 15 18:51:21 systemvm passwd_server_ip.py[1507]:   File "/opt/cloud/bin/passwd_server_ip.py", line 120, in do_GET
Jan 15 18:51:21 systemvm passwd_server_ip.py[1507]:     self.wfile.write(password)
Jan 15 18:51:21 systemvm passwd_server_ip.py[1507]:   File "/usr/lib/python3.9/socketserver.py", line 799, in write
Jan 15 18:51:21 systemvm passwd_server_ip.py[1507]:     self._sock.sendall(b)
Jan 15 18:51:21 systemvm passwd_server_ip.py[1507]: TypeError: a bytes-like object is required, not 'str'
```

* python3: fix self.cl.get_router_password in Redundant VRs

```
File "/opt/cloud/bin/cs/CsDatabag.py", line 154, in get_router_password
    md5.update(passwd)
TypeError: Unicode-objects must be encoded before hashing"]
```

* scripts: mark multipath scripts as executable

* systemvm template: remove hyperv packages and do not export

* VR: update default RAM size of System VMs/VRs to 512MiB

Before
```
mysql> select id,name,cpu,speed,ram_size,unique_name,system_use from service_offering where name like "System%";
+----+----------------------------------------------------------+------+-------+----------+----------------------------------+------------+
| id | name                                                     | cpu  | speed | ram_size | unique_name                      | system_use |
+----+----------------------------------------------------------+------+-------+----------+----------------------------------+------------+
|  3 | System Offering For Software Router                      |    1 |   500 |      256 | Cloud.Com-SoftwareRouter         |          1 |
|  4 | System Offering For Software Router - Local Storage      |    1 |   500 |      256 | Cloud.Com-SoftwareRouter-Local   |          1 |
|  5 | System Offering For Internal LB VM                       |    1 |   256 |      256 | Cloud.Com-InternalLBVm           |          1 |
|  6 | System Offering For Internal LB VM - Local Storage       |    1 |   256 |      256 | Cloud.Com-InternalLBVm-Local     |          1 |
|  7 | System Offering For Console Proxy                        |    1 |   500 |     1024 | Cloud.com-ConsoleProxy           |          1 |
|  8 | System Offering For Console Proxy - Local Storage        |    1 |   500 |     1024 | Cloud.com-ConsoleProxy-Local     |          1 |
|  9 | System Offering For Secondary Storage VM                 |    1 |   500 |      512 | Cloud.com-SecondaryStorage       |          1 |
| 10 | System Offering For Secondary Storage VM - Local Storage |    1 |   500 |      512 | Cloud.com-SecondaryStorage-Local |          1 |
| 11 | System Offering For Elastic LB VM                        |    1 |   128 |      128 | Cloud.Com-ElasticLBVm            |          1 |
| 12 | System Offering For Elastic LB VM - Local Storage        |    1 |   128 |      128 | Cloud.Com-ElasticLBVm-Local      |          1 |
+----+----------------------------------------------------------+------+-------+----------+----------------------------------+------------+
10 rows in set (0.00 sec)
```

New value
```
mysql> select id,name,cpu,speed,ram_size,unique_name,system_use from service_offering where name like "System%";
+----+----------------------------------------------------------+------+-------+----------+----------------------------------+------------+
| id | name                                                     | cpu  | speed | ram_size | unique_name                      | system_use |
+----+----------------------------------------------------------+------+-------+----------+----------------------------------+------------+
|  3 | System Offering For Software Router                      |    1 |   500 |      512 | Cloud.Com-SoftwareRouter         |          1 |
|  4 | System Offering For Software Router - Local Storage      |    1 |   500 |      512 | Cloud.Com-SoftwareRouter-Local   |          1 |
|  5 | System Offering For Internal LB VM                       |    1 |   256 |      512 | Cloud.Com-InternalLBVm           |          1 |
|  6 | System Offering For Internal LB VM - Local Storage       |    1 |   256 |      512 | Cloud.Com-InternalLBVm-Local     |          1 |
|  7 | System Offering For Console Proxy                        |    1 |   500 |     1024 | Cloud.com-ConsoleProxy           |          1 |
|  8 | System Offering For Console Proxy - Local Storage        |    1 |   500 |     1024 | Cloud.com-ConsoleProxy-Local     |          1 |
|  9 | System Offering For Secondary Storage VM                 |    1 |   500 |      512 | Cloud.com-SecondaryStorage       |          1 |
| 10 | System Offering For Secondary Storage VM - Local Storage |    1 |   500 |      512 | Cloud.com-SecondaryStorage-Local |          1 |
| 11 | System Offering For Elastic LB VM                        |    1 |   128 |      512 | Cloud.Com-ElasticLBVm            |          1 |
| 12 | System Offering For Elastic LB VM - Local Storage        |    1 |   128 |      512 | Cloud.Com-ElasticLBVm-Local      |          1 |
+----+----------------------------------------------------------+------+-------+----------+----------------------------------+------------+
10 rows in set (0.01 sec)
```

* debian12: fix test_network_ipv6 and test_vpc_ipv6

* python3: remove duplicated imports

* debian12: failed to start Apache2 server (SSLCipherSuite @SECLEVEL=0)

error message
```
[Sat Jan 20 22:51:14.595143 2024] [ssl:emerg] [pid 10200:tid 140417063888768] AH02562: Failed to configure certificate cloudinternal.com:443:0 (with chain), check /etc/ssl/certs/cert_apache.crt
[Sat Jan 20 22:51:14.595234 2024] [ssl:emerg] [pid 10200:tid 140417063888768] SSL Library Error: error:0A00018E:SSL routines::ca md too weak
AH00016: Configuration Failed
```

openssl version
```
root@s-167-VM:~# openssl version -a
OpenSSL 3.0.11 19 Sep 2023 (Library: OpenSSL 3.0.11 19 Sep 2023)
built on: Mon Oct 23 17:52:22 2023 UTC
platform: debian-amd64
options:  bn(64,64)
compiler: gcc -fPIC -pthread -m64 -Wa,--noexecstack -Wall -fzero-call-used-regs=used-gpr -DOPENSSL_TLS_SECURITY_LEVEL=2 -Wa,--noexecstack -g -O2 -ffile-prefix-map=/build/reproducible-path/openssl-3.0.11=. -fstack-protector-strong -Wformat -Werror=format-security -DOPENSSL_USE_NODELETE -DL_ENDIAN -DOPENSSL_PIC -DOPENSSL_BUILDING_OPENSSL -DNDEBUG -Wdate-time -D_FORTIFY_SOURCE=2
OPENSSLDIR: "/usr/lib/ssl"
ENGINESDIR: "/usr/lib/x86_64-linux-gnu/engines-3"
MODULESDIR: "/usr/lib/x86_64-linux-gnu/ossl-modules"
Seeding source: os-specific
CPUINFO: OPENSSL_ia32cap=0x80202001478bfffd:0x0
```

certificate
```
root@s-167-VM:~# keytool -printcert -rfc -file /usr/local/cloud/systemvm/certs/realhostip.crt
-----BEGIN CERTIFICATE-----
MIIFZTCCBE2gAwIBAgIHKBCduBUoKDANBgkqhkiG9w0BAQUFADCByjELMAkGA1UE
BhMCVVMxEDAOBgNVBAgTB0FyaXpvbmExEzARBgNVBAcTClNjb3R0c2RhbGUxGjAY
BgNVBAoTEUdvRGFkZHkuY29tLCBJbmMuMTMwMQYDVQQLEypodHRwOi8vY2VydGlm
aWNhdGVzLmdvZGFkZHkuY29tL3JlcG9zaXRvcnkxMDAuBgNVBAMTJ0dvIERhZGR5
IFNlY3VyZSBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTERMA8GA1UEBRMIMDc5Njky
ODcwHhcNMTIwMjAzMDMzMDQwWhcNMTcwMjA3MDUxMTIzWjBZMRkwFwYDVQQKDBAq
LnJlYWxob3N0aXAuY29tMSEwHwYDVQQLDBhEb21haW4gQ29udHJvbCBWYWxpZGF0
ZWQxGTAXBgNVBAMMECoucmVhbGhvc3RpcC5jb20wggEiMA0GCSqGSIb3DQEBAQUA
A4IBDwAwggEKAoIBAQCDT9AtEfs+s/I8QXp6rrCw0iNJ0+GgsybNHheU+JpL39LM
TZykCrZhZnyDvwdxCoOfE38Sa32baHKNds+y2SHnMNsOkw8OcNucHEBX1FIpOBGp
h9D6xC+umx9od6xMWETUv7j6h2u+WC3OhBM8fHCBqIiAol31/IkcqDxxsHlQ8S/o
CfTlXJUY6Yn628OA1XijKdRnadV0hZ829cv/PZKljjwQUTyrd0KHQeksBH+YAYSo
2JUl8ekNLsOi8/cPtfojnltzRI1GXi0ZONs8VnDzJ0a2gqZY+uxlz+CGbLnGnlN4
j9cBpE+MfUE+35Dq121sTpsSgF85Mz+pVhn2S633AgMBAAGjggG+MIIBujAPBgNV
HRMBAf8EBTADAQEAMB0GA1UdJQQWMBQGCCsGAQUFBwMBBggrBgEFBQcDAjAOBgNV
HQ8BAf8EBAMCBaAwMwYDVR0fBCwwKjAooCagJIYiaHR0cDovL2NybC5nb2RhZGR5
LmNvbS9nZHMxLTY0LmNybDBTBgNVHSAETDBKMEgGC2CGSAGG/W0BBxcBMDkwNwYI
KwYBBQUHAgEWK2h0dHA6Ly9jZXJ0aWZpY2F0ZXMuZ29kYWRkeS5jb20vcmVwb3Np
dG9yeS8wgYAGCCsGAQUFBwEBBHQwcjAkBggrBgEFBQcwAYYYaHR0cDovL29jc3Au
Z29kYWRkeS5jb20vMEoGCCsGAQUFBzAChj5odHRwOi8vY2VydGlmaWNhdGVzLmdv
ZGFkZHkuY29tL3JlcG9zaXRvcnkvZ2RfaW50ZXJtZWRpYXRlLmNydDAfBgNVHSME
GDAWgBT9rGEyk2xF1uLuhV+auud2mWjM5zArBgNVHREEJDAighAqLnJlYWxob3N0
aXAuY29tgg5yZWFsaG9zdGlwLmNvbTAdBgNVHQ4EFgQUZyJz9/QLy5TWIIscTXID
E8Xk47YwDQYJKoZIhvcNAQEFBQADggEBAKiUV3KK16mP0NpS92fmQkCLqm+qUWyN
BfBVgf9/M5pcT8EiTZlS5nAtzAE/eRpBeR3ubLlaAogj4rdH7YYVJcDDLLoB2qM3
qeCHu8LFoblkb93UuFDWqRaVPmMlJRnhsRkL1oa2gM2hwQTkBDkP7w5FG1BELCgl
gZI2ij2yxjge6pOEwSyZCzzbCcg9pN+dNrYyGEtB4k+BBnPA3N4r14CWbk+uxjrQ
6j2Ip+b7wOc5IuMEMl8xwTyjuX3lsLbAZyFI9RCyofwA9NqIZ1GeB6Zd196rubQp
93cmBqGGjZUs3wMrGlm7xdjlX6GQ9UvmvkMub9+lL99A5W50QgCmFeI=
-----END CERTIFICATE-----

Warning:
The certificate uses the SHA1withRSA signature algorithm which is considered a security risk. This algorithm will be disabled in a future update.
```

it comes from
```
$ openssl x509 -in ./systemvm/agent/certs/realhostip.crt -noout -text
Certificate:
    Data:
        Version: 3 (0x2)
        Serial Number: 11277268652730408 (0x28109db8152828)
        Signature Algorithm: sha1WithRSAEncryption
        Issuer: C = US, ST = Arizona, L = Scottsdale, O = "GoDaddy.com, Inc.", OU = http://certificates.godaddy.com/repository, CN = Go Daddy Secure Certification Authority, serialNumber = 07969287
        Validity
            Not Before: Feb  3 03:30:40 2012 GMT
            Not After : Feb  7 05:11:23 2017 GMT
        Subject: O = *.realhostip.com, OU = Domain Control Validated, CN = *.realhostip.com
```

* debian12: use ed25519 instead of rsa as ssh-rsa has been deprecated in OpenSSH

on xenserver
```
[root@pr8497-t8906-xenserver-71-xs2 ~]# ssh -i .ssh/id_rsa.cloud -p 3922 169.254.214.153
Warning: Permanently added '[169.254.214.153]:3922' (ECDSA) to the list of known hosts.
Permission denied (publickey).
```
in the CPVM
Jan 22 19:31:09 v-1-VM sshd[2869]: userauth_pubkey: signature algorithm ssh-rsa not in PubkeyAcceptedAlgorithms [preauth]
Jan 22 19:31:09 v-1-VM sshd[2869]: Connection closed by authenticating user root 169.254.0.1 port 54704 [preauth]
```

ssh-dss (DSA) is not supported either

* debian12: add PubkeyAcceptedAlgorithms=+ssh-rsa to sshd_config

* VR: install python3 packages in case of Debian 11

* pom.xml: exclude systemvm/agent/packages/* in license check

* systemvm: do not patch router/systemvm during startup

this will cause 4.19 SYSTEM template not work, but may be expected
- python3 VS python2 (default)
- openSSL 3.0.1 VS 1.1.1w
- openssh-server 9.1 VS 8.4

* VR: patch router/systemvm if template is debian11

This supports debian 11 template by
- revert change in systemvm/debian/etc/ssh/sshd_config
- patch VR/systemvms during startup
- install packages during patching system vm/routers

* python3 flake: fix E502 the backslash is redundant between brackets

```
../debian/root/health_checks/router_version_check.py:55:70: E502 the backslash is redundant between brackets
../debian/root/health_checks/router_version_check.py:58:61: E502 the backslash is redundant between brackets
../debian/root/health_checks/router_version_check.py:67:71: E502 the backslash is redundant between brackets
../debian/root/health_checks/router_version_check.py:70:60: E502 the backslash is redundant between brackets
../debian/root/health_checks/haproxy_check.py:47:71: E502 the backslash is redundant between brackets
../debian/root/health_checks/haproxy_check.py:48:64: E502 the backslash is redundant between brackets
../debian/root/health_checks/cpu_usage_check.py:43:54: E502 the backslash is redundant between brackets
../debian/root/health_checks/cpu_usage_check.py:46:58: E502 the backslash is redundant between brackets
../debian/root/health_checks/memory_usage_check.py:31:65: E502 the backslash is redundant between brackets
../debian/root/health_checks/memory_usage_check.py:42:57: E502 the backslash is redundant between brackets
../debian/root/health_checks/memory_usage_check.py:45:63: E502 the backslash is redundant between brackets
```

* python3 flake: fix E275 missing whitespace after keyword

```
../debian/opt/cloud/bin/cs_firewallrules.py:29:20: E275 missing whitespace after keyword
../debian/opt/cloud/bin/cs_dhcp.py:27:16: E275 missing whitespace after keyword
../debian/opt/cloud/bin/cs_dhcp.py:36:16: E275 missing whitespace after keyword
../debian/opt/cloud/bin/cs_guestnetwork.py:33:20: E275 missing whitespace after keyword
../debian/opt/cloud/bin/cs_guestnetwork.py:35:16: E275 missing whitespace after keyword
../debian/opt/cloud/bin/cs_vpnusers.py:37:16: E275 missing whitespace after keyword
../debian/opt/cloud/bin/merge.py:230:11: E275 missing whitespace after keyword
../debian/opt/cloud/bin/merge.py:239:19: E275 missing whitespace after keyword
../debian/opt/cloud/bin/cs_remoteaccessvpn.py:24:12: E275 missing whitespace after keyword
../debian/opt/cloud/bin/cs_site2sitevpn.py:24:12: E275 missing whitespace after keyword
../debian/opt/cloud/bin/cs/CsHelper.py:90:15: E275 missing whitespace after keyword
../debian/opt/cloud/bin/cs/CsAddress.py:367:15: E275 missing whitespace after keyword
```

* python3 flake: fix configure.py

```
../debian/opt/cloud/bin/configure.py:24:22: E401 multiple imports on one line
../debian/opt/cloud/bin/configure.py:43:180: E501 line too long (294 > 179 characters)
../debian/opt/cloud/bin/configure.py:46:1: E302 expected 2 blank lines, found 1
../debian/opt/cloud/bin/configure.py:63:1: E302 expected 2 blank lines, found 1
../debian/opt/cloud/bin/configure.py:65:12: E721 do not compare types, for exact checks use `is` / `is not`, for instance checks use `isinstance()`
../debian/opt/cloud/bin/configure.py:72:1: E302 expected 2 blank lines, found 1
../debian/opt/cloud/bin/configure.py:310:25: E711 comparison to None should be 'if cond is not None:'
../debian/opt/cloud/bin/configure.py:312:29: E711 comparison to None should be 'if cond is None:'
../debian/opt/cloud/bin/configure.py:378:25: E711 comparison to None should be 'if cond is not None:'
../debian/opt/cloud/bin/configure.py:380:29: E711 comparison to None should be 'if cond is None:'
../debian/opt/cloud/bin/configure.py:490:29: E712 comparison to False should be 'if cond is False:' or 'if not cond:'
../debian/opt/cloud/bin/configure.py:642:16: E721 do not compare types, for exact checks use `is` / `is not`, for instance checks use `isinstance()`
../debian/opt/cloud/bin/configure.py:644:18: E721 do not compare types, for exact checks use `is` / `is not`, for instance checks use `isinstance()`
../debian/opt/cloud/bin/configure.py:1416:1: E305 expected 2 blank lines after class or function definition, found 1
```

* python3 flake: fix other python files

```
../debian/opt/cloud/bin/vmdata.py:97:12: E721 do not compare types, for exact checks use `is` / `is not`, for instance checks use `isinstance()`
../debian/opt/cloud/bin/vmdata.py:99:14: E721 do not compare types, for exact checks use `is` / `is not`, for instance checks use `isinstance()`

../debian/opt/cloud/bin/cs/CsRedundant.py:438:53: E203 whitespace before ':'
../debian/opt/cloud/bin/cs/CsRedundant.py:461:53: E203 whitespace before ':'
../debian/opt/cloud/bin/cs/CsRedundant.py:499:5: E303 too many blank lines (2)

../debian/opt/cloud/bin/cs/CsDatabag.py:189:1: E302 expected 2 blank lines, found 1
../debian/opt/cloud/bin/cs/CsDatabag.py:193:37: E721 do not compare types, for exact checks use `is` / `is not`, for instance checks use `isinstance()`

../debian/opt/cloud/bin/cs/CsHelper.py:118:30: E231 missing whitespace after ','
../debian/opt/cloud/bin/cs/CsHelper.py:119:15: E225 missing whitespace around operator
../debian/opt/cloud/bin/cs/CsHelper.py:127:19: E225 missing whitespace around operator

../debian/opt/cloud/bin/cs/CsAddress.py:324:43: E221 multiple spaces before operator

../debian/opt/cloud/bin/cs/CsVpcGuestNetwork.py:28:1: E302 expected 2 blank lines, found 1
```

* python3 flake: fix CsNetfilter.py

```
../debian/opt/cloud/bin/cs/CsNetfilter.py:226:13: E117 over-indented
../debian/opt/cloud/bin/cs/CsNetfilter.py:233:180: E501 line too long (197 > 179 characters)
../debian/opt/cloud/bin/cs/CsNetfilter.py:241:14: E201 whitespace after '{'
../debian/opt/cloud/bin/cs/CsNetfilter.py:242:14: E201 whitespace after '{'
../debian/opt/cloud/bin/cs/CsNetfilter.py:247:18: E201 whitespace after '{'
../debian/opt/cloud/bin/cs/CsNetfilter.py:247:74: E202 whitespace before '}'
../debian/opt/cloud/bin/cs/CsNetfilter.py:248:18: E201 whitespace after '{'
```

* systemvm/test: fix sys.path

```
$ bash runtests.sh
/usr/bin/python
Python 3.10.12
Running pycodestyle to check systemvm/python code for errors
Running pylint to check systemvm/python code for errors
Python 3.10.12
pylint 2.12.2
astroid 2.9.3
Python 3.10.12 (main, Nov 20 2023, 15:14:05) [GCC 11.4.0]

--------------------------------------------------------------------
Your code has been rated at 10.00/10 (previous run: 10.00/10, +0.00)

--------------------------------------------------------------------
Your code has been rated at 10.00/10 (previous run: 10.00/10, +0.00)

Running systemvm/python unit tests
....Device "eth0" does not exist.
.....................
----------------------------------------------------------------------
Ran 25 tests in 0.008s

OK
```

* Revert "systemvm template: remove hyperv packages and do not export"

This reverts commit 4383d59d031bde6eae7ebba261ff641ca0a66cd5.

* debian12: move SQL change to schema-41900to42000.sql

* debian12: update systemvm template version to 4.20 in pom.xml

* pom.xml: fix NPE if templates do not exist on download.cloudstack.org

* debian12: increase default system offering for routers to 384MiB RAM

* CKS: fix addkubernetessupportedversion failed with JRE17

```
marvin.cloudstackException.CloudstackAPIException: Execute cmd: addkubernetessupportedversion failed, due to: errorCode: 530, errorText:Cannot invoke "org.apache.cloudstack.engine.subsystem.api.storage.ObjectInDataStoreStateMachine$State.toString()" because the return value of "com.cloud.api.query.vo.TemplateJoinVO.getState()" is null
```

* python3: revert changes by 2to3 with systemvm/debian/root/health_checks/*.py

* debian12: use ISO/packages on download.cloudstack.org

* VR: Update default ram size to 384

* debian12: fix router_version_check.py after VR live-patch and add health check in test_routers.py

* debian12: fix build error after log4j 2.x merge

* VR: Update default ram size to 512MB (again)

This reverts commit 578dd2b73f380e8231ae1eb59827230757cac5e8 and efafa8c4d63775653a2cd406fca10784fbcec3e3.

* systemvmtemplate: Upgrade to Debian 12.5.0

* systemvm template: increase swap to 512MB

* VR: fix health check error due to deprecated SafeConfigParser

warning below
```
root@r-20-VM:~# /opt/cloud/bin/getRouterMonitorResults.sh true
/root/monitorServices.py:59: DeprecationWarning: The SafeConfigParser class has been renamed to ConfigParser in Python 3.2. This alias will be removed in Python 3.12. Use ConfigParser directly instead.
  parser = SafeConfigParser()
```

* test: fix wget does not work in macchinina vms on vmware80u1

fixes error below
```
{Cmd: wget -t 1 -T 1 www.google.com via Host: 10.0.55.186} {returns: ["wget: '/usr/lib/libpcre.so.1' is not an ELF file", "wget: can't load library 'libpcre.so.1'"]}
```

* packaging: add message for VR memory upgrade after packages installation

---------

Signed-off-by: Rohit Yadav <rohit.yadav@shapeblue.com>
Co-authored-by: Rohit Yadav <rohit.yadav@shapeblue.com>
Co-authored-by: Vishesh <vishesh92@gmail.com>
2024-02-26 18:07:50 +05:30

815 lines
35 KiB
Python

# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing,
# software distributed under the License is distributed on an
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.
""" BVT test for IPv6 Network"""
#Import Local Modules
from marvin.codes import FAILED
from marvin.cloudstackTestCase import cloudstackTestCase
from marvin.cloudstackAPI import (createGuestNetworkIpv6Prefix,
listGuestNetworkIpv6Prefixes,
deleteGuestNetworkIpv6Prefix,
listIpv6FirewallRules,
createIpv6FirewallRule,
deleteIpv6FirewallRule)
from marvin.lib.utils import (random_gen,
get_process_status,
get_host_credentials)
from marvin.lib.base import (Configurations,
Domain,
NetworkOffering,
Account,
PublicIpRange,
Network,
Router,
ServiceOffering,
VirtualMachine,
NIC,
Host)
from marvin.lib.common import (get_domain,
get_zone,
list_hosts,
get_test_template)
from marvin.sshClient import SshClient
from marvin.cloudstackException import CloudstackAPIException
from marvin.lib.decoratorGenerators import skipTestIf
from nose.plugins.attrib import attr
from ipaddress import IPv6Network
from random import getrandbits, choice, randint
import time
import logging
import threading
ipv6_offering_config_name = "ipv6.offering.enabled"
ULA_BASE = IPv6Network("fd00::/8")
PREFIX_OPTIONS = [i for i in range(48, 65, 4)]
FIREWALL_TABLE = "ip6_firewall"
FIREWALL_CHAINS = {
"Ingress": "fw_chain_ingress",
"Egress": "fw_chain_egress"
}
CIDR_IPV6_ANY = "::/0"
ICMPV6_TYPE = {
1: "destination-unreachable",
2: "packet-too-big",
3: "time-exceeded",
4: "parameter-problem",
128: "echo-request",
129: "echo-reply",
130: "mld-listener-query",
131: "mld-listener-report",
132: "mld-listener-done",
133: "nd-router-solicit",
134: "nd-router-advert",
135: "nd-neighbor-solicit",
136: "nd-neighbor-advert",
137: "nd-redirect",
138: "router-renumbering",
141: "ind-neighbor-solicit",
142: "ind-neighbor-advert",
143: "mld2-listener-report"
}
ICMPV6_CODE_TYPE = {
0: "no-route",
1: "admin-prohibited",
3: "addr-unreachable",
4: "port-unreachable",
5: "policy-fail",
6: "reject-route"
}
ICMPV6_TYPE_ANY = "{ destination-unreachable, packet-too-big, time-exceeded, parameter-problem, echo-request, echo-reply, mld-listener-query, mld-listener-report, mld-listener-done, nd-router-solicit, nd-router-advert, nd-neighbor-solicit, nd-neighbor-advert, nd-redirect, router-renumbering }"
TCP_UDP_PORT_ANY = "{ 0-65535 }"
SLEEP_BEFORE_VR_CHANGES = 45
PING_RETRIES = 5
PING_SLEEP = 20
class TestIpv6Network(cloudstackTestCase):
@classmethod
def setUpClass(cls):
testClient = super(TestIpv6Network, cls).getClsTestClient()
cls.services = testClient.getParsedTestDataConfig()
cls.apiclient = testClient.getApiClient()
cls.dbclient = testClient.getDbConnection()
cls.test_ipv6_guestprefix = None
cls.initial_ipv6_offering_enabled = None
cls._cleanup = []
cls.routerDetailsMap = {}
cls.logger = logging.getLogger('TestIpv6Network')
cls.zone = get_zone(cls.apiclient, testClient.getZoneForTests())
cls.services['mode'] = cls.zone.networktype
cls.ipv6NotSupported = False
ipv6_guestprefix = cls.getGuestIpv6Prefix()
if ipv6_guestprefix == None:
cls.ipv6NotSupported = True
if cls.ipv6NotSupported == False:
ipv6_publiciprange = cls.getPublicIpv6Range()
if ipv6_publiciprange == None:
cls.ipv6NotSupported = True
if cls.ipv6NotSupported == False:
cls.initial_ipv6_offering_enabled = Configurations.list(
cls.apiclient,
name=ipv6_offering_config_name)[0].value
Configurations.update(cls.apiclient,
ipv6_offering_config_name,
"true")
cls.domain = get_domain(cls.apiclient)
cls.account = Account.create(
cls.apiclient,
cls.services["account"],
admin=False,
domainid=cls.domain.id
)
cls._cleanup.append(cls.account)
cls.hypervisor = testClient.getHypervisorInfo()
cls.template = get_test_template(
cls.apiclient,
cls.zone.id,
cls.hypervisor)
else:
cls.debug("IPv6 is not supported, skipping tests!")
return
@classmethod
def tearDownClass(cls):
if cls.initial_ipv6_offering_enabled != None:
Configurations.update(cls.apiclient,
ipv6_offering_config_name,
cls.initial_ipv6_offering_enabled)
try:
super(TestIpv6Network, cls).tearDownClass()
finally:
if cls.test_ipv6_guestprefix != None:
cmd = deleteGuestNetworkIpv6Prefix.deleteGuestNetworkIpv6PrefixCmd()
cmd.id = cls.test_ipv6_guestprefix.id
cls.apiclient.deleteGuestNetworkIpv6Prefix(cmd)
@classmethod
def getGuestIpv6Prefix(cls):
cmd = listGuestNetworkIpv6Prefixes.listGuestNetworkIpv6PrefixesCmd()
cmd.zoneid = cls.zone.id
ipv6_prefixes_response = cls.apiclient.listGuestNetworkIpv6Prefixes(cmd)
if isinstance(ipv6_prefixes_response, list) == True and len(ipv6_prefixes_response) > 0:
return ipv6_prefixes_response[0]
ipv6_guestprefix_service = cls.services["guestip6prefix"]
cmd = createGuestNetworkIpv6Prefix.createGuestNetworkIpv6PrefixCmd()
cmd.zoneid = cls.zone.id
cmd.prefix = ipv6_guestprefix_service["prefix"]
ipv6_guestprefix = cls.apiclient.createGuestNetworkIpv6Prefix(cmd)
cls.test_ipv6_guestprefix = ipv6_guestprefix
return ipv6_guestprefix
@classmethod
def getPublicIpv6Range(cls):
list_public_ip_range_response = PublicIpRange.list(
cls.apiclient,
zoneid=cls.zone.id
)
ipv4_range_vlan = None
if isinstance(list_public_ip_range_response, list) == True and len(list_public_ip_range_response) > 0:
for ip_range in list_public_ip_range_response:
if ip_range.ip6cidr != None and ip_range.ip6gateway != None:
return ip_range
if ip_range.netmask != None and ip_range.gateway != None:
vlan = ip_range.vlan
if ipv4_range_vlan == None and vlan.startswith("vlan://"):
vlan = vlan.replace("vlan://", "")
if vlan == "untagged":
ipv4_range_vlan = None
else:
ipv4_range_vlan = int(vlan)
ipv6_publiciprange_service = cls.services["publicip6range"]
ipv6_publiciprange_service["zoneid"] = cls.zone.id
ipv6_publiciprange_service["vlan"] = ipv4_range_vlan
ipv6_publiciprange = PublicIpRange.create(
cls.apiclient,
ipv6_publiciprange_service
)
cls._cleanup.append(ipv6_publiciprange)
return ipv6_publiciprange
def setUp(self):
self.services = self.testClient.getParsedTestDataConfig()
self.apiclient = self.testClient.getApiClient()
self.userapiclient = self.testClient.getUserApiClient(
UserName=self.account.name,
DomainName=self.account.domain
)
self.dbclient = self.testClient.getDbConnection()
self.thread = None
self.cleanup = []
return
def tearDown(self):
try:
if self.thread and self.thread.is_alive():
self.thread.join(5*60)
except Exception as e:
raise Exception("Warning: Exception during cleanup : %s" % e)
finally:
super(TestIpv6Network, self).tearDown()
return
def getRandomIpv6Cidr(self):
prefix_length = choice(PREFIX_OPTIONS)
random_suffix = getrandbits(40) << (128-prefix_length)
base_address = ULA_BASE.network_address + random_suffix
return str(IPv6Network((base_address, prefix_length)))
def createTinyServiceOffering(self):
self.service_offering = ServiceOffering.create(
self.apiclient,
self.services["service_offerings"]["big"],
)
self.cleanup.append(self.service_offering)
def createNetworkOfferingInternal(self, is_redundant, is_ipv6, egressdefaultpolicy=True):
off_service = self.services["network_offering"]
if is_redundant:
off_service = self.services["nw_off_isolated_RVR"]
if is_ipv6:
off_service["internetprotocol"] = "dualstack"
if egressdefaultpolicy:
off_service["egress_policy"] = egressdefaultpolicy
network_offering = NetworkOffering.create(
self.apiclient,
off_service
)
self.cleanup.append(network_offering)
network_offering.update(self.apiclient, state='Enabled')
return network_offering
def createIpv4NetworkOffering(self, is_redundant=False):
self.network_offering = self.createNetworkOfferingInternal(is_redundant, False, False)
def createIpv6NetworkOffering(self, is_redundant=False):
self.network_offering = self.createNetworkOfferingInternal(is_redundant, True, False)
def createIpv6NetworkOfferingForUpdate(self, is_redundant=False):
self.network_offering_update = self.createNetworkOfferingInternal(is_redundant, True)
def deployNetwork(self):
self.services["network"]["networkoffering"] = self.network_offering.id
self.network = Network.create(
self.userapiclient,
self.services["network"],
zoneid=self.zone.id
)
self.cleanup.append(self.network)
def deployNetworkVm(self):
if self.template == FAILED:
assert False, "get_test_template() failed to return template"
self.services["virtual_machine"]["zoneid"] = self.zone.id
self.virtual_machine = VirtualMachine.create(
self.userapiclient,
self.services["virtual_machine"],
templateid=self.template.id,
networkids=self.network.id,
serviceofferingid=self.service_offering.id
)
self.cleanup.append(self.virtual_machine)
def checkIpv6NetworkBasic(self):
self.debug("Listing network: %s" % (self.network.name))
ipv6_network = Network.list(self.apiclient,listall="true",id=self.network.id)
self.assertTrue(
isinstance(ipv6_network, list),
"Check listNetworks response returns a valid list"
)
self.assertEqual(
len(ipv6_network),
1,
"Network not found"
)
ipv6_network = ipv6_network[0]
self.assertNotEqual(ipv6_network,
None,
"User is not able to retrieve network details %s" % self.network.id)
self.assertNotEqual(ipv6_network.ip6cidr,
None,
"IPv6 CIDR for network is empty")
self.assertNotEqual(ipv6_network.ip6gateway,
None,
"IPv6 gateway for network is empty")
self.assertNotEqual(ipv6_network.ip6routes,
None,
"IPv6 routes for network is empty")
self.network_ipv6_routes = ipv6_network.ip6routes
def checkIpv6NetworkRoutersBasic(self):
self.debug("Listing routers for network: %s" % self.network.name)
self.routers = Router.list(
self.apiclient,
networkid=self.network.id,
listall=True
)
self.assertTrue(
isinstance(self.routers, list),
"Check listRouters response returns a valid list"
)
self.assertTrue(
len(self.routers) > 0,
"Router for the network isn't found"
)
for router in self.routers:
self.assertFalse(
router.isredundantrouter == True and router.redundantstate == "FAULT",
"Router for the network is in FAULT state"
)
nics = router.nic
for nic in nics:
if (nic.traffictype == 'Guest' and router.isredundantrouter == False) or nic.traffictype == 'Public':
self.assertNotEqual(nic.ip6address,
None,
"IPv6 address for router %s NIC is empty" % nic.traffictype)
self.assertNotEqual(nic.ip6cidr,
None,
"IPv6 CIDR for router %s NIC is empty" % nic.traffictype)
self.assertNotEqual(nic.ip6gateway,
None,
"IPv6 gateway for router %s NIC is empty" % nic.traffictype)
def getRouterProcessStatus(self, router, cmd):
if router.id not in self.routerDetailsMap or self.routerDetailsMap[router.id] is None:
connect_ip = self.apiclient.connection.mgtSvr
connect_user = self.apiclient.connection.user
connect_passwd = self.apiclient.connection.passwd
hypervisor = self.hypervisor
if self.hypervisor.lower() not in ('vmware', 'hyperv'):
hosts = Host.list(
self.apiclient,
zoneid=router.zoneid,
type='Routing',
state='Up',
id=router.hostid
)
self.assertEqual(
isinstance(hosts, list),
True,
"Check list host returns a valid list"
)
host = hosts[0]
connect_ip = host.ipaddress
hypervisor = None
try:
connect_user, connect_passwd= get_host_credentials(
self.config, host.ipaddress)
except KeyError:
self.skipTest(
"Marvin configuration has no host credentials to\
check router services")
details = {}
details['connect_ip'] = connect_ip
details['connect_user'] = connect_user
details['connect_passwd'] = connect_passwd
details['hypervisor'] = hypervisor
self.routerDetailsMap[router.id] = details
result = get_process_status(
self.routerDetailsMap[router.id]['connect_ip'],
22,
self.routerDetailsMap[router.id]['connect_user'],
self.routerDetailsMap[router.id]['connect_passwd'],
router.linklocalip,
cmd,
hypervisor=self.routerDetailsMap[router.id]['hypervisor']
)
self.assertTrue(type(result) == list and len(result) > 0,
"%s on router %s returned invalid result" % (cmd, router.id))
result = '\n'.join(result)
return result
def getNetworkRouter(self, network, red_state="PRIMARY"):
routers = Router.list(
self.apiclient,
networkid=network.id,
listall=True
)
self.assertTrue(
isinstance(routers, list) and len(routers) > 0,
"No routers found for network %s" % network.id
)
if len(routers) == 1:
return routers[0]
for router in routers:
if router.redundantstate == red_state:
return router
def getNetworkGateway(self, network):
ipv6_network = Network.list(self.apiclient,listall="true",id=network.id)
self.assertTrue(
isinstance(ipv6_network, list),
"Check listNetworks response returns a valid list"
)
self.assertEqual(
len(ipv6_network),
1,
"Network not found"
)
ipv6_network = ipv6_network[0]
self.assertNotEqual(ipv6_network.ip6gateway,
None,
"IPv6 gateway for network is empty")
return ipv6_network.ip6gateway
def getNetworkRoutes(self, network):
ipv6_network = Network.list(self.apiclient,listall="true",id=network.id)
self.assertTrue(
isinstance(ipv6_network, list),
"Check listNetworks response returns a valid list"
)
self.assertEqual(
len(ipv6_network),
1,
"Network not found"
)
ipv6_network = ipv6_network[0]
self.assertNotEqual(ipv6_network.ip6routes,
None,
"IPv6 routes for network is empty")
return ipv6_network.ip6routes
def isNetworkEgressDefaultPolicyAllow(self, network):
ipv6_network = Network.list(self.apiclient,listall="true",id=network.id)
if len(ipv6_network) == 1:
ipv6_network = ipv6_network[0]
return ipv6_network.egressdefaultpolicy
return False
def checkRouterNicState(self, router, dev, state):
st = "state %s" % state
cmd = "ip link show %s | grep '%s'" % (dev, st)
res = self.getRouterProcessStatus(router, cmd)
self.assertTrue(type(res) == str and len(res) > 0 and st in res,
"%s failed on router %s" % (cmd, router.id))
def checkIpv6NetworkPrimaryRouter(self, router, network_ip6gateway):
self.checkRouterNicState(router, "eth0", "UP")
guest_gateway_check_cmd = "ip -6 address show %s | grep 'inet6 %s'" % ("eth0", network_ip6gateway)
res = self.getRouterProcessStatus(router, guest_gateway_check_cmd)
self.assertTrue(type(res) == str and len(res) > 0 and network_ip6gateway in res,
"%s failed on router %s" % (guest_gateway_check_cmd, router.id))
self.assertFalse("dadfailed" in res,
"dadfailed for IPv6 guest gateway on router %s" % router.id)
self.checkRouterNicState(router, "eth2", "UP")
public_ipv6 = None
public_ipv6_gateway = None
nics = router.nic
for nic in nics:
if nic.traffictype == 'Public':
public_ipv6 = nic.ip6address
public_ipv6_gateway = nic.ip6gateway
break
self.assertNotEqual(public_ipv6,
None,
"IPv6 address for router Public NIC is empty")
public_ip_check_cmd = "ip -6 address show %s | grep 'inet6 %s'" % ("eth2", public_ipv6)
res = self.getRouterProcessStatus(router, public_ip_check_cmd)
self.assertTrue(type(res) == str and len(res) > 0 and public_ipv6 in res,
"%s failed on router %s" % (public_ip_check_cmd, router.id))
self.assertFalse("dadfailed" in res,
"dadfailed for public IPv6 on router %s" % router.id)
self.assertNotEqual(public_ipv6_gateway,
None,
"IPv6 gateway for router Public NIC is empty")
default_route_check_cmd = "ip -6 route | grep 'default via %s'" % (public_ipv6_gateway)
res = self.getRouterProcessStatus(router, default_route_check_cmd)
self.assertTrue(type(res) == str and len(res) > 0 and public_ipv6_gateway in res,
"%s failed on router %s" % (default_route_check_cmd, router.id))
def checkIpv6NetworkBackupRouter(self, router, network_ip6gateway):
self.checkRouterNicState(router, "eth0", "UP")
guest_gateway_check_cmd = "ip -6 address show %s | grep 'inet6 %s'" % ("eth0", network_ip6gateway)
res = self.getRouterProcessStatus(router, guest_gateway_check_cmd)
self.assertFalse(type(res) == str and len(res) > 0 and network_ip6gateway in res,
"%s failed on router %s" % (guest_gateway_check_cmd, router.id))
self.checkRouterNicState(router, "eth2", "DOWN")
def checkIpv6NetworkRoutersInternal(self):
network_ip6gateway = self.getNetworkGateway(self.network)
for router in self.routers:
if router.state != "Running":
continue
if router.isredundantrouter == True and router.redundantstate == 'BACKUP':
self.checkIpv6NetworkBackupRouter(router, network_ip6gateway)
continue
self.checkIpv6NetworkPrimaryRouter(router, network_ip6gateway)
def checkIpv6NetworkVm(self):
self.debug("Listing NICS for VM %s in network: %s" % (self.virtual_machine.name, self.network.name))
nics = NIC.list(
self.apiclient,
virtualmachineid=self.virtual_machine.id,
networkid=self.network.id
)
self.assertEqual(
len(nics),
1,
"VM NIC for the network isn't found"
)
nic = nics[0]
self.assertNotEqual(nic.ip6address,
None,
"IPv6 address for VM %s NIC is empty" % nic.traffictype)
self.virtual_machine_ipv6_address = nic.ip6address
self.assertNotEqual(nic.ip6cidr,
None,
"IPv6 CIDR for VM %s NIC is empty" % nic.traffictype)
self.assertNotEqual(nic.ip6gateway,
None,
"IPv6 gateway for VM %s NIC is empty" % nic.traffictype)
def restartNetworkWithCleanup(self):
self.network.restart(self.userapiclient, cleanup=True)
time.sleep(SLEEP_BEFORE_VR_CHANGES)
def updateNetworkWithOffering(self):
self.network.update(self.userapiclient, networkofferingid=self.network_offering_update.id)
time.sleep(SLEEP_BEFORE_VR_CHANGES)
def createIpv6FirewallRuleInNetwork(self, network_id, traffic_type, source_cidr, dest_cidr, protocol,
start_port, end_port, icmp_type, icmp_code):
cmd = createIpv6FirewallRule.createIpv6FirewallRuleCmd()
cmd.networkid = network_id
cmd.traffictype = traffic_type
if source_cidr:
cmd.cidrlist = source_cidr
if dest_cidr:
cmd.destcidrlist = dest_cidr
if protocol:
cmd.protocol = protocol
if start_port:
cmd.startport = start_port
if end_port:
cmd.endport = end_port
if icmp_type is not None:
cmd.icmptype = icmp_type
if icmp_code is not None:
cmd.icmpcode = icmp_code
fw_rule = self.userapiclient.createIpv6FirewallRule(cmd)
return fw_rule
def deployRoutingTestResources(self):
self.routing_test_network_offering = self.createNetworkOfferingInternal(False, True)
self.services["network"]["networkoffering"] = self.routing_test_network_offering.id
self.routing_test_network = Network.create(
self.apiclient,
self.services["network"],
self.account.name,
self.account.domainid,
zoneid=self.zone.id
)
self.cleanup.append(self.routing_test_network)
self.services["virtual_machine"]["zoneid"] = self.zone.id
self.routing_test_vm = VirtualMachine.create(
self.apiclient,
self.services["virtual_machine"],
templateid=self.template.id,
accountid=self.account.name,
domainid=self.account.domainid,
networkids=[self.routing_test_network.id],
serviceofferingid=self.service_offering.id,
mode="advanced"
)
self.cleanup.append(self.routing_test_vm)
def prepareRoutingTestResourcesInBackground(self):
self.thread = threading.Thread(target=self.deployRoutingTestResources, args=())
self.thread.daemon = True
self.thread.start()
def checkIpv6NetworkRouting(self):
if not self.thread:
self.deployRoutingTestResources()
else:
self.thread.join(5*60)
self.assertFalse(not self.routing_test_network or not self.routing_test_vm,
"Routing resources failure")
fw1 = self.createIpv6FirewallRuleInNetwork(self.routing_test_network.id, "Ingress", None, None, "icmp",
None, None, None, None)
fw2 = self.createIpv6FirewallRuleInNetwork(self.network.id, "Ingress", None, None, "icmp",
None, None, None, None)
test_network_router = self.getNetworkRouter(self.routing_test_network)
routes = self.getNetworkRoutes(self.network)
self.logger.debug("Adding network routes in routing_test_network %s" % routes)
for route in routes:
add_route_cmd = "ip -6 route add %s via %s" % (route.subnet, route.gateway)
self.getRouterProcessStatus(test_network_router, add_route_cmd)
network_router = self.getNetworkRouter(self.network)
routes = self.getNetworkRoutes(self.routing_test_network)
self.logger.debug("Adding routing_test_network routes in network %s" % routes)
for route in routes:
add_route_cmd = "ip -6 route add %s via %s" % (route.subnet, route.gateway)
self.getRouterProcessStatus(network_router, add_route_cmd)
ping_cmd = "ping6 -c 4 %s" % self.virtual_machine_ipv6_address
count = 0
while count < PING_RETRIES:
count = count + 1
res = self.getRouterProcessStatus(test_network_router, ping_cmd)
if " 0% packet loss" in res:
break
time.sleep(PING_SLEEP)
self.assertTrue(" 0% packet loss" in res,
"Ping from router %s of network %s to VM %s of network %s is unsuccessful" % (test_network_router.id, self.routing_test_network.id, self.virtual_machine.id, self.network.id))
ssh = self.routing_test_vm.get_ssh_client(retries=5)
count = 0
while count < PING_RETRIES:
count = count + 1
res = ssh.execute(ping_cmd)
if type(res) == list and len(res) > 0 and " 0% packet loss" in '\n'.join(res):
break
time.sleep(PING_SLEEP)
self.assertTrue(type(res) == list and len(res) > 0,
"%s on VM %s returned invalid result" % (ping_cmd, self.routing_test_vm.id))
self.logger.debug(res)
res = '\n'.join(res)
self.assertTrue(" 0% packet loss" in res,
"Ping from VM %s of network %s to VM %s of network %s is unsuccessful" % (self.routing_test_vm.id, self.routing_test_network.id, self.virtual_machine.id, self.network.id))
cmd = deleteIpv6FirewallRule.deleteIpv6FirewallRuleCmd()
cmd.id = fw2.id
self.userapiclient.deleteIpv6FirewallRule(cmd)
def createAndVerifyIpv6FirewallRule(self, traffic_type, source_cidr, dest_cidr, protocol,
start_port, end_port, icmp_type, icmp_code, parsed_rule, delete=False):
self.logger.debug("createAndVerifyIpv6FirewallRule - %s" % parsed_rule)
fw_rule = self.createIpv6FirewallRuleInNetwork(self.network.id, traffic_type, source_cidr, dest_cidr, protocol,
start_port, end_port, icmp_type, icmp_code)
cmd = listIpv6FirewallRules.listIpv6FirewallRulesCmd()
cmd.id = fw_rule.id
rules = self.userapiclient.listIpv6FirewallRules(cmd)
self.assertTrue(
isinstance(rules, list),
"Check listIpv6FirewallRules response returns a valid list"
)
rule = rules[0]
self.assertEqual(rule.networkid, self.network.id,
"IPv6 firewall rule network ID mismatch %s, %s" % (rule.networkid, self.network.id))
self.assertEqual(rule.traffictype, traffic_type,
"IPv6 firewall rule traffic type mismatch %s, %s" % (rule.traffictype, traffic_type))
if source_cidr:
self.assertEqual(rule.cidrlist, source_cidr,
"IPv6 firewall rule source CIDR mismatch %s, %s" % (rule.cidrlist, source_cidr))
if dest_cidr:
self.assertEqual(rule.destcidrlist, dest_cidr,
"IPv6 firewall rule destination CIDR mismatch %s, %s" % (rule.destcidrlist, dest_cidr))
if protocol:
self.assertEqual(rule.protocol, protocol,
"IPv6 firewall rule protocol mismatch %s, %s" % (rule.protocol, protocol))
if start_port:
self.assertEqual(rule.startport, start_port,
"IPv6 firewall rule start port mismatch %d, %d" % (rule.startport, start_port))
if end_port:
self.assertEqual(rule.endport, end_port,
"IPv6 firewall rule end port mismatch %d, %d" % (rule.endport, end_port))
if icmp_type is not None:
self.assertEqual(rule.icmptype, icmp_type,
"IPv6 firewall rule ICMP type mismatch %d, %d" % (rule.icmptype, icmp_type))
if icmp_code is not None:
self.assertEqual(rule.icmpcode, icmp_code,
"IPv6 firewall rule ICMP code mismatch %d, %d" % (rule.icmpcode, icmp_code))
routerCmd = "nft list chain ip6 %s %s" % (FIREWALL_TABLE, FIREWALL_CHAINS[traffic_type])
res = self.getRouterProcessStatus(self.getNetworkRouter(self.network), routerCmd)
parsed_rule_new = parsed_rule.replace("{ ", "").replace(" }", "")
self.assertTrue(parsed_rule in res or parsed_rule_new in res,
"Listing firewall rule with nft list chain failure for rule: '%s' is not in '%s'" % (parsed_rule, res))
if delete == True:
cmd = deleteIpv6FirewallRule.deleteIpv6FirewallRuleCmd()
cmd.id = fw_rule.id
self.userapiclient.deleteIpv6FirewallRule(cmd)
res = self.getRouterProcessStatus(self.getNetworkRouter(self.network), routerCmd)
self.assertFalse(parsed_rule in res or parsed_rule_new in res,
"Firewall rule present in nft list chain failure despite delete for rule: '%s' is in '%s'" % (parsed_rule, res))
def checkIpv6FirewallRule(self):
traffic_type = "Ingress"
# Ingress - ip6 saddr SOURCE_CIDR ip6 daddr DEST_CIDR tcp dport { START_PORT-END_PORT } accept
source_cidr = self.getRandomIpv6Cidr()
dest_cidr = self.getRandomIpv6Cidr()
protocol = "tcp"
start_port = randint(3000, 5000)
end_port = start_port + randint(1, 8)
rule = "ip6 saddr %s ip6 daddr %s %s dport { %d-%d } accept" % (source_cidr, dest_cidr, protocol, start_port, end_port)
self.createAndVerifyIpv6FirewallRule(traffic_type, source_cidr, dest_cidr, protocol,
start_port, end_port, None, None, rule, True)
# Ingress - ip6 daddr DEST_CIDR icmpv6 type TYPE code CODE accept
source_cidr = self.getRandomIpv6Cidr()
protocol = "icmp"
icmp_type = choice(list(ICMPV6_TYPE.keys()))
icmp_code = choice(list(ICMPV6_CODE_TYPE.keys()))
rule = "ip6 saddr %s ip6 daddr %s %sv6 type %s %sv6 code %s accept" % (source_cidr, CIDR_IPV6_ANY, protocol, ICMPV6_TYPE[icmp_type], protocol, ICMPV6_CODE_TYPE[icmp_code])
self.createAndVerifyIpv6FirewallRule(traffic_type, source_cidr, None, protocol,
None, None, icmp_type, icmp_code, rule)
action = "accept"
if self.isNetworkEgressDefaultPolicyAllow(self.network):
action = "drop"
traffic_type = "Egress"
# Egress - ip6 saddr ::/0 ip6 daddr ::/0 udp dport { 0-65355 } ACTION
protocol = "udp"
rule = "ip6 saddr %s ip6 daddr %s %s dport %s %s" % (CIDR_IPV6_ANY, CIDR_IPV6_ANY, protocol, TCP_UDP_PORT_ANY, action)
self.createAndVerifyIpv6FirewallRule(traffic_type, None, None, protocol,
None, None, None, None, rule)
# Egress - ip6 saddr ::/0 ip6 daddr ::/0 icmpv6 type ANY_TYPE ACTION
protocol = "icmp"
rule = "ip6 saddr %s ip6 daddr %s %sv6 type %s %s" % (CIDR_IPV6_ANY, CIDR_IPV6_ANY, protocol, ICMPV6_TYPE_ANY, action)
self.createAndVerifyIpv6FirewallRule(traffic_type, None, None, protocol,
None, None, None, None, rule)
# Egress - ip6 saddr ::/0 ip6 daddr DEST_CIDR ACTION
protocol = "all"
dest_cidr = self.getRandomIpv6Cidr()
rule = "ip6 saddr %s ip6 daddr %s %s" % (CIDR_IPV6_ANY, CIDR_IPV6_ANY, action)
self.createAndVerifyIpv6FirewallRule(traffic_type, None, None, protocol,
None, None, None, None, rule)
def checkNetworkVRRedundancy(self):
network_ip6gateway = self.getNetworkGateway(self.network)
primary_router = self.getNetworkRouter(self.network)
Router.stop(
self.apiclient,
id=primary_router.id
)
time.sleep(SLEEP_BEFORE_VR_CHANGES)
new_primary_router = self.getNetworkRouter(self.network)
self.assertNotEqual(new_primary_router.id, primary_router.id,
"Original primary router ID: %s of network is still the primary router after stopping" % (primary_router.id))
self.checkIpv6NetworkPrimaryRouter(new_primary_router, network_ip6gateway)
def checkIpv6Network(self):
self.checkIpv6NetworkBasic()
self.checkIpv6NetworkRoutersBasic()
self.checkIpv6NetworkRoutersInternal()
@attr(
tags=[
"advanced",
"basic",
"eip",
"sg",
"advancedns",
"smoke"],
required_hardware="false")
@skipTestIf("ipv6NotSupported")
def test_01_verify_ipv6_network(self):
"""Test to verify IPv6 network
# Validate the following:
# 1. Create IPv6 network, deploy VM
# 2. Verify network has required IPv6 details
# 3. List router for the network and verify it has required IPv6 details for Guest and Public NIC of the VR
# 4. SSH into VR(s) and verify correct details are present for its NICs
# 5. Verify VM in network has required IPv6 details
# 6. Restart network with cleanup and re-verify network details
# 7. Update network with a new offering and re-verify network details
# 8. Deploy another IPv6 network and check routing between two networks and their VM
# 9. Create IPv6 firewall rules and verify in VR if they get implemented
"""
self.createIpv6NetworkOffering()
self.createIpv6NetworkOfferingForUpdate()
self.createTinyServiceOffering()
self.deployNetwork()
self.deployNetworkVm()
self.checkIpv6Network()
self.checkIpv6NetworkVm()
self.prepareRoutingTestResourcesInBackground()
self.restartNetworkWithCleanup()
self.checkIpv6Network()
self.updateNetworkWithOffering()
self.checkIpv6Network()
self.checkIpv6NetworkRouting()
self.checkIpv6FirewallRule()