cloud-setup: merge branch 'lr/more-cloud-setup-tests'
https://gitlab.freedesktop.org/NetworkManager/NetworkManager/-/merge_requests/1606
This commit is contained in:
@@ -21,6 +21,13 @@
|
||||
#define nm_auto_unref_ip_address nm_auto(_nm_ip_address_unref)
|
||||
NM_AUTO_DEFINE_FCN0(NMIPAddress *, _nm_ip_address_unref, nm_ip_address_unref);
|
||||
|
||||
static inline NMIPRoute *
|
||||
_nm_ip_route_ref(NMIPRoute *route)
|
||||
{
|
||||
nm_ip_route_ref(route);
|
||||
return route;
|
||||
}
|
||||
|
||||
#define nm_auto_unref_ip_route nm_auto(_nm_auto_unref_ip_route)
|
||||
NM_AUTO_DEFINE_FCN0(NMIPRoute *, _nm_auto_unref_ip_route, nm_ip_route_unref);
|
||||
|
||||
|
@@ -315,8 +315,9 @@ _nmc_mangle_connection(NMDevice *device,
|
||||
addrs_new = g_ptr_array_new_full(config_data->ipv4s_len, (GDestroyNotify) nm_ip_address_unref);
|
||||
rules_new =
|
||||
g_ptr_array_new_full(config_data->ipv4s_len, (GDestroyNotify) nm_ip_routing_rule_unref);
|
||||
routes_new = g_ptr_array_new_full(config_data->iproutes_len + !!config_data->ipv4s_len,
|
||||
(GDestroyNotify) nm_ip_route_unref);
|
||||
routes_new =
|
||||
g_ptr_array_new_full(nm_g_ptr_array_len(config_data->iproutes) + !!config_data->ipv4s_len,
|
||||
(GDestroyNotify) nm_ip_route_unref);
|
||||
|
||||
if (remote_s_ip) {
|
||||
guint len;
|
||||
@@ -422,8 +423,8 @@ _nmc_mangle_connection(NMDevice *device,
|
||||
}
|
||||
}
|
||||
|
||||
for (i = 0; i < config_data->iproutes_len; ++i)
|
||||
g_ptr_array_add(routes_new, config_data->iproutes_arr[i]);
|
||||
for (i = 0; i < nm_g_ptr_array_len(config_data->iproutes); i++)
|
||||
g_ptr_array_add(routes_new, _nm_ip_route_ref(config_data->iproutes->pdata[i]));
|
||||
|
||||
addrs_changed = nmcs_setting_ip_replace_ipv4_addresses(s_ip,
|
||||
(NMIPAddress **) addrs_new->pdata,
|
||||
|
@@ -17,8 +17,30 @@
|
||||
#define NM_AZURE_METADATA_URL_BASE /* $NM_AZURE_BASE/$NM_AZURE_API_VERSION */ \
|
||||
"/metadata/instance/network/interface/"
|
||||
|
||||
static const char *
|
||||
_azure_base(void)
|
||||
{
|
||||
static const char *base_cached = NULL;
|
||||
const char *base;
|
||||
|
||||
again:
|
||||
base = g_atomic_pointer_get(&base_cached);
|
||||
if (G_UNLIKELY(!base)) {
|
||||
/* The base URI can be set via environment variable.
|
||||
* This is mainly for testing, it's not usually supposed to be configured.
|
||||
* Consider this private API! */
|
||||
base = g_getenv(NMCS_ENV_VARIABLE("NM_CLOUD_SETUP_AZURE_HOST"));
|
||||
base = nmcs_utils_uri_complete_interned(base) ?: ("" NM_AZURE_BASE);
|
||||
|
||||
if (!g_atomic_pointer_compare_and_exchange(&base_cached, NULL, base))
|
||||
goto again;
|
||||
}
|
||||
|
||||
return base;
|
||||
}
|
||||
|
||||
#define _azure_uri_concat(...) \
|
||||
nmcs_utils_uri_build_concat(NM_AZURE_BASE, __VA_ARGS__, NM_AZURE_API_VERSION)
|
||||
nmcs_utils_uri_build_concat(_azure_base(), __VA_ARGS__, NM_AZURE_API_VERSION)
|
||||
#define _azure_uri_interfaces(...) _azure_uri_concat(NM_AZURE_METADATA_URL_BASE, ##__VA_ARGS__)
|
||||
|
||||
/*****************************************************************************/
|
||||
|
@@ -13,15 +13,39 @@
|
||||
#define HTTP_POLL_TIMEOUT_MS 10000
|
||||
#define HTTP_RATE_LIMIT_MS 1000
|
||||
|
||||
#define NM_GCP_HOST "metadata.google.internal"
|
||||
#define NM_GCP_BASE "http://" NM_GCP_HOST
|
||||
#define NM_GCP_API_VERSION "/v1"
|
||||
#define NM_GCP_METADATA_URL_BASE NM_GCP_BASE "/computeMetadata" NM_GCP_API_VERSION "/instance"
|
||||
#define NM_GCP_METADATA_URL_NET "/network-interfaces/"
|
||||
#define NM_GCP_HOST "metadata.google.internal"
|
||||
#define NM_GCP_BASE "http://" NM_GCP_HOST
|
||||
#define NM_GCP_API_VERSION "/v1"
|
||||
#define NM_GCP_METADATA_URL_NET "/network-interfaces/"
|
||||
|
||||
#define NM_GCP_METADATA_HEADER "Metadata-Flavor: Google"
|
||||
|
||||
#define _gcp_uri_concat(...) nmcs_utils_uri_build_concat(NM_GCP_METADATA_URL_BASE, __VA_ARGS__)
|
||||
static const char *
|
||||
_gcp_base(void)
|
||||
{
|
||||
static const char *base_cached = NULL;
|
||||
const char *base;
|
||||
|
||||
again:
|
||||
base = g_atomic_pointer_get(&base_cached);
|
||||
if (G_UNLIKELY(!base)) {
|
||||
/* The base URI can be set via environment variable.
|
||||
* This is mainly for testing, it's not usually supposed to be configured.
|
||||
* Consider this private API! */
|
||||
base = g_getenv(NMCS_ENV_VARIABLE("NM_CLOUD_SETUP_GCP_HOST"));
|
||||
base = nmcs_utils_uri_complete_interned(base) ?: ("" NM_GCP_BASE);
|
||||
|
||||
if (!g_atomic_pointer_compare_and_exchange(&base_cached, NULL, base))
|
||||
goto again;
|
||||
}
|
||||
|
||||
return base;
|
||||
}
|
||||
|
||||
#define _gcp_uri_concat(...) \
|
||||
nmcs_utils_uri_build_concat(_gcp_base(), \
|
||||
"/computeMetadata" NM_GCP_API_VERSION "/instance", \
|
||||
__VA_ARGS__)
|
||||
#define _gcp_uri_interfaces(...) _gcp_uri_concat(NM_GCP_METADATA_URL_NET, ##__VA_ARGS__)
|
||||
|
||||
/*****************************************************************************/
|
||||
@@ -73,7 +97,7 @@ detect(NMCSProvider *provider, GTask *task)
|
||||
http_client = nmcs_provider_get_http_client(provider);
|
||||
|
||||
nm_http_client_poll_req(http_client,
|
||||
(uri = _gcp_uri_concat("id")),
|
||||
(uri = _gcp_uri_concat("/id")),
|
||||
HTTP_TIMEOUT_MS,
|
||||
256 * 1024,
|
||||
7000,
|
||||
@@ -112,7 +136,6 @@ _get_config_fip_cb(GObject *source, GAsyncResult *result, gpointer user_data)
|
||||
GCPIfaceData *iface_data = user_data;
|
||||
gs_free_error GError *error = NULL;
|
||||
gs_free char *ipaddr = NULL;
|
||||
NMIPRoute **routes_arr;
|
||||
NMIPRoute *route_new;
|
||||
|
||||
nm_http_client_poll_req_finish(NM_HTTP_CLIENT(source), result, NULL, &response, &error);
|
||||
@@ -137,15 +160,14 @@ _get_config_fip_cb(GObject *source, GAsyncResult *result, gpointer user_data)
|
||||
ipaddr);
|
||||
|
||||
iface_get_config = iface_data->iface_get_config;
|
||||
routes_arr = iface_get_config->iproutes_arr;
|
||||
|
||||
route_new = nm_ip_route_new(AF_INET, ipaddr, 32, NULL, 100, &error);
|
||||
if (error)
|
||||
goto out_done;
|
||||
|
||||
nm_ip_route_set_attribute(route_new, NM_IP_ROUTE_ATTRIBUTE_TYPE, g_variant_new_string("local"));
|
||||
routes_arr[iface_get_config->iproutes_len] = route_new;
|
||||
++iface_get_config->iproutes_len;
|
||||
|
||||
g_ptr_array_add(iface_get_config->iproutes, route_new);
|
||||
|
||||
out_done:
|
||||
if (!error) {
|
||||
@@ -215,7 +237,8 @@ _get_config_ips_list_cb(GObject *source, GAsyncResult *result, gpointer user_dat
|
||||
goto out_error;
|
||||
}
|
||||
|
||||
iface_data->iface_get_config->iproutes_arr = g_new(NMIPRoute *, iface_data->n_fips_pending);
|
||||
iface_data->iface_get_config->iproutes =
|
||||
g_ptr_array_new_full(iface_data->n_fips_pending, (GDestroyNotify) nm_ip_route_unref);
|
||||
|
||||
for (i = 0; i < uri_arr->len; ++i) {
|
||||
const char *str = uri_arr->pdata[i];
|
||||
|
@@ -216,7 +216,7 @@ _iface_data_free(gpointer data)
|
||||
NMCSProviderGetConfigIfaceData *iface_data = data;
|
||||
|
||||
g_free(iface_data->ipv4s_arr);
|
||||
g_free(iface_data->iproutes_arr);
|
||||
nm_g_ptr_array_unref(iface_data->iproutes);
|
||||
g_free((char *) iface_data->hwaddr);
|
||||
|
||||
nm_g_slice_free(iface_data);
|
||||
|
@@ -34,8 +34,8 @@ typedef struct {
|
||||
bool has_cidr : 1;
|
||||
bool has_gateway : 1;
|
||||
|
||||
NMIPRoute **iproutes_arr;
|
||||
gsize iproutes_len;
|
||||
/* Array of NMIPRoute (must own/free the entries). */
|
||||
GPtrArray *iproutes;
|
||||
|
||||
/* TRUE, if the configuration was requested via hwaddrs argument to
|
||||
* nmcs_provider_get_config(). */
|
||||
@@ -59,7 +59,8 @@ static inline gboolean
|
||||
nmcs_provider_get_config_iface_data_is_valid(const NMCSProviderGetConfigIfaceData *config_data)
|
||||
{
|
||||
return config_data && config_data->iface_idx >= 0
|
||||
&& ((config_data->has_ipv4s && config_data->has_cidr) || config_data->iproutes_len);
|
||||
&& ((config_data->has_ipv4s && config_data->has_cidr)
|
||||
|| nm_g_ptr_array_len(config_data->iproutes) > 0);
|
||||
}
|
||||
|
||||
/*****************************************************************************/
|
||||
|
@@ -147,6 +147,7 @@ except ImportError:
|
||||
try:
|
||||
from http.server import HTTPServer
|
||||
from http.server import BaseHTTPRequestHandler
|
||||
from http.client import HTTPConnection, HTTPResponse
|
||||
except ImportError:
|
||||
HTTPServer = None
|
||||
|
||||
@@ -1009,6 +1010,8 @@ class TestNmClient(unittest.TestCase):
|
||||
|
||||
pexp = pexpect.spawn(argv[0], argv[1:], timeout=10, env=env)
|
||||
|
||||
pexp.str_last_chars = 100000
|
||||
|
||||
typ = collections.namedtuple("CallPexpect", ["pexp", "valgrind_log"])
|
||||
return typ(pexp, valgrind_log)
|
||||
|
||||
@@ -2137,6 +2140,13 @@ class TestNmcli(TestNmClient):
|
||||
|
||||
|
||||
class TestNmCloudSetup(TestNmClient):
|
||||
|
||||
_mac1 = "9e:c0:3e:92:24:2d"
|
||||
_mac2 = "53:e9:7e:52:8d:a8"
|
||||
|
||||
_ip1 = "172.31.26.249"
|
||||
_ip2 = "172.31.176.249"
|
||||
|
||||
def cloud_setup_test(func):
|
||||
"""
|
||||
Runs the mock NetworkManager along with a mock cloud metadata service.
|
||||
@@ -2160,34 +2170,47 @@ class TestNmCloudSetup(TestNmClient):
|
||||
# hallucinogenic substances.
|
||||
s.listen(5)
|
||||
|
||||
def pass_socket():
|
||||
os.dup2(s.fileno(), 3)
|
||||
|
||||
service_path = PathConfiguration.test_cloud_meta_mock_path()
|
||||
env = os.environ.copy()
|
||||
env["LISTEN_FD"] = str(s.fileno())
|
||||
env["LISTEN_FDS"] = "1"
|
||||
p = subprocess.Popen(
|
||||
[sys.executable, service_path],
|
||||
[sys.executable, service_path, "--empty"],
|
||||
stdin=subprocess.PIPE,
|
||||
env=env,
|
||||
pass_fds=(s.fileno(),),
|
||||
pass_fds=(3,),
|
||||
preexec_fn=pass_socket,
|
||||
)
|
||||
|
||||
self.md_url = "http://%s:%d" % s.getsockname()
|
||||
(hostaddr, port) = s.getsockname()
|
||||
self.md_conn = HTTPConnection(hostaddr, port=port)
|
||||
self.md_url = "http://%s:%d" % (hostaddr, port)
|
||||
s.close()
|
||||
|
||||
error = None
|
||||
|
||||
self.srv_start()
|
||||
func(self)
|
||||
try:
|
||||
func(self)
|
||||
except Exception as e:
|
||||
error = e
|
||||
self._nm_test_post()
|
||||
|
||||
self.md_conn.close()
|
||||
p.stdin.close()
|
||||
p.terminate()
|
||||
p.wait()
|
||||
|
||||
if error:
|
||||
raise error
|
||||
|
||||
return f
|
||||
|
||||
@cloud_setup_test
|
||||
def test_ec2(self):
|
||||
|
||||
def _mock_devices(self):
|
||||
# Add a device with an active connection that has IPv4 configured
|
||||
self.srv.op_AddObj("WiredDevice", iface="eth0")
|
||||
self.srv.op_AddObj("WiredDevice", iface="eth0", mac="9e:c0:3e:92:24:2d")
|
||||
self.srv.addAndActivateConnection(
|
||||
{
|
||||
"connection": {"type": "802-3-ethernet", "id": "con-eth0"},
|
||||
@@ -2198,7 +2221,7 @@ class TestNmCloudSetup(TestNmClient):
|
||||
)
|
||||
|
||||
# The second connection has no IPv4
|
||||
self.srv.op_AddObj("WiredDevice", iface="eth1")
|
||||
self.srv.op_AddObj("WiredDevice", iface="eth1", mac="53:e9:7e:52:8d:a8")
|
||||
self.srv.addAndActivateConnection(
|
||||
{"connection": {"type": "802-3-ethernet", "id": "con-eth1"}},
|
||||
"/org/freedesktop/NetworkManager/Devices/2",
|
||||
@@ -2206,6 +2229,210 @@ class TestNmCloudSetup(TestNmClient):
|
||||
delay=0,
|
||||
)
|
||||
|
||||
def _mock_path(self, path, body):
|
||||
self.md_conn.request("PUT", path, body=body)
|
||||
self.md_conn.getresponse().read()
|
||||
|
||||
@cloud_setup_test
|
||||
def test_aliyun(self):
|
||||
self._mock_devices()
|
||||
|
||||
_aliyun_meta = "/2016-01-01/meta-data/"
|
||||
_aliyun_macs = _aliyun_meta + "network/interfaces/macs/"
|
||||
self._mock_path(_aliyun_meta, "ami-id\n")
|
||||
self._mock_path(
|
||||
_aliyun_macs, TestNmCloudSetup._mac2 + "\n" + TestNmCloudSetup._mac1
|
||||
)
|
||||
self._mock_path(
|
||||
_aliyun_macs + TestNmCloudSetup._mac2 + "/vpc-cidr-block", "172.31.16.0/20"
|
||||
)
|
||||
self._mock_path(
|
||||
_aliyun_macs + TestNmCloudSetup._mac2 + "/private-ipv4s",
|
||||
TestNmCloudSetup._ip1,
|
||||
)
|
||||
self._mock_path(
|
||||
_aliyun_macs + TestNmCloudSetup._mac2 + "/primary-ip-address",
|
||||
TestNmCloudSetup._ip1,
|
||||
)
|
||||
self._mock_path(
|
||||
_aliyun_macs + TestNmCloudSetup._mac2 + "/netmask", "255.255.255.0"
|
||||
)
|
||||
self._mock_path(
|
||||
_aliyun_macs + TestNmCloudSetup._mac2 + "/gateway", "172.31.26.2"
|
||||
)
|
||||
self._mock_path(
|
||||
_aliyun_macs + TestNmCloudSetup._mac1 + "/vpc-cidr-block", "172.31.166.0/20"
|
||||
)
|
||||
self._mock_path(
|
||||
_aliyun_macs + TestNmCloudSetup._mac1 + "/private-ipv4s",
|
||||
TestNmCloudSetup._ip2,
|
||||
)
|
||||
self._mock_path(
|
||||
_aliyun_macs + TestNmCloudSetup._mac1 + "/primary-ip-address",
|
||||
TestNmCloudSetup._ip2,
|
||||
)
|
||||
self._mock_path(
|
||||
_aliyun_macs + TestNmCloudSetup._mac1 + "/netmask", "255.255.255.0"
|
||||
)
|
||||
self._mock_path(
|
||||
_aliyun_macs + TestNmCloudSetup._mac1 + "/gateway", "172.31.176.2"
|
||||
)
|
||||
|
||||
# Run nm-cloud-setup for the first time
|
||||
nmc = self.call_pexpect(
|
||||
ENV_NM_TEST_CLIENT_CLOUD_SETUP_PATH,
|
||||
[],
|
||||
{
|
||||
"NM_CLOUD_SETUP_ALIYUN_HOST": self.md_url,
|
||||
"NM_CLOUD_SETUP_LOG": "trace",
|
||||
"NM_CLOUD_SETUP_ALIYUN": "yes",
|
||||
},
|
||||
)
|
||||
|
||||
nmc.pexp.expect("provider aliyun detected")
|
||||
nmc.pexp.expect("found interfaces: 9E:C0:3E:92:24:2D, 53:E9:7E:52:8D:A8")
|
||||
nmc.pexp.expect("get-config: start fetching meta data")
|
||||
nmc.pexp.expect("get-config: success")
|
||||
nmc.pexp.expect("meta data received")
|
||||
# One of the devices has no IPv4 configuration to be modified
|
||||
nmc.pexp.expect("device has no suitable applied connection. Skip")
|
||||
# The other one was lacking an address set it up.
|
||||
nmc.pexp.expect("some changes were applied for provider aliyun")
|
||||
nmc.pexp.expect(pexpect.EOF)
|
||||
|
||||
# Run nm-cloud-setup for the second time
|
||||
nmc = self.call_pexpect(
|
||||
ENV_NM_TEST_CLIENT_CLOUD_SETUP_PATH,
|
||||
[],
|
||||
{
|
||||
"NM_CLOUD_SETUP_ALIYUN_HOST": self.md_url,
|
||||
"NM_CLOUD_SETUP_LOG": "trace",
|
||||
"NM_CLOUD_SETUP_ALIYUN": "yes",
|
||||
},
|
||||
)
|
||||
|
||||
nmc.pexp.expect("provider aliyun detected")
|
||||
nmc.pexp.expect("found interfaces: 9E:C0:3E:92:24:2D, 53:E9:7E:52:8D:A8")
|
||||
nmc.pexp.expect("get-config: starting")
|
||||
nmc.pexp.expect("get-config: success")
|
||||
nmc.pexp.expect("meta data received")
|
||||
# No changes this time
|
||||
nmc.pexp.expect('device needs no update to applied connection "con-eth0"')
|
||||
nmc.pexp.expect("no changes were applied for provider aliyun")
|
||||
nmc.pexp.expect(pexpect.EOF)
|
||||
|
||||
Util.valgrind_check_log(nmc.valgrind_log, "test_aliyun")
|
||||
|
||||
@cloud_setup_test
|
||||
def test_azure(self):
|
||||
self._mock_devices()
|
||||
|
||||
_azure_meta = "/metadata/instance"
|
||||
_azure_iface = _azure_meta + "/network/interface/"
|
||||
_azure_query = "?format=text&api-version=2017-04-02"
|
||||
self._mock_path(_azure_meta + _azure_query, "")
|
||||
self._mock_path(_azure_iface + _azure_query, "0\n1\n")
|
||||
self._mock_path(
|
||||
_azure_iface + "0/macAddress" + _azure_query, TestNmCloudSetup._mac1
|
||||
)
|
||||
self._mock_path(
|
||||
_azure_iface + "1/macAddress" + _azure_query, TestNmCloudSetup._mac2
|
||||
)
|
||||
self._mock_path(_azure_iface + "0/ipv4/ipAddress/" + _azure_query, "0\n")
|
||||
self._mock_path(_azure_iface + "1/ipv4/ipAddress/" + _azure_query, "0\n")
|
||||
self._mock_path(
|
||||
_azure_iface + "0/ipv4/ipAddress/0/privateIpAddress" + _azure_query,
|
||||
TestNmCloudSetup._ip1,
|
||||
)
|
||||
self._mock_path(
|
||||
_azure_iface + "1/ipv4/ipAddress/0/privateIpAddress" + _azure_query,
|
||||
TestNmCloudSetup._ip2,
|
||||
)
|
||||
self._mock_path(
|
||||
_azure_iface + "0/ipv4/subnet/0/address/" + _azure_query, "172.31.16.0"
|
||||
)
|
||||
self._mock_path(
|
||||
_azure_iface + "1/ipv4/subnet/0/address/" + _azure_query, "172.31.166.0"
|
||||
)
|
||||
self._mock_path(_azure_iface + "0/ipv4/subnet/0/prefix/" + _azure_query, "20")
|
||||
self._mock_path(_azure_iface + "1/ipv4/subnet/0/prefix/" + _azure_query, "20")
|
||||
|
||||
# Run nm-cloud-setup for the first time
|
||||
nmc = self.call_pexpect(
|
||||
ENV_NM_TEST_CLIENT_CLOUD_SETUP_PATH,
|
||||
[],
|
||||
{
|
||||
"NM_CLOUD_SETUP_AZURE_HOST": self.md_url,
|
||||
"NM_CLOUD_SETUP_LOG": "trace",
|
||||
"NM_CLOUD_SETUP_AZURE": "yes",
|
||||
},
|
||||
)
|
||||
|
||||
nmc.pexp.expect("provider azure detected")
|
||||
nmc.pexp.expect("found interfaces: 9E:C0:3E:92:24:2D, 53:E9:7E:52:8D:A8")
|
||||
nmc.pexp.expect("found azure interfaces: 2")
|
||||
nmc.pexp.expect("interface\[0]: found a matching device with hwaddr")
|
||||
nmc.pexp.expect(
|
||||
"interface\[0]: (received subnet address|received subnet prefix 20)"
|
||||
)
|
||||
nmc.pexp.expect(
|
||||
"interface\[0]: (received subnet address|received subnet prefix 20)"
|
||||
)
|
||||
nmc.pexp.expect("get-config: success")
|
||||
nmc.pexp.expect("meta data received")
|
||||
# One of the devices has no IPv4 configuration to be modified
|
||||
nmc.pexp.expect("device has no suitable applied connection. Skip")
|
||||
# The other one was lacking an address set it up.
|
||||
nmc.pexp.expect("some changes were applied for provider azure")
|
||||
nmc.pexp.expect(pexpect.EOF)
|
||||
|
||||
# Run nm-cloud-setup for the second time
|
||||
nmc = self.call_pexpect(
|
||||
ENV_NM_TEST_CLIENT_CLOUD_SETUP_PATH,
|
||||
[],
|
||||
{
|
||||
"NM_CLOUD_SETUP_AZURE_HOST": self.md_url,
|
||||
"NM_CLOUD_SETUP_LOG": "trace",
|
||||
"NM_CLOUD_SETUP_AZURE": "yes",
|
||||
},
|
||||
)
|
||||
|
||||
nmc.pexp.expect("provider azure detected")
|
||||
nmc.pexp.expect("found interfaces: 9E:C0:3E:92:24:2D, 53:E9:7E:52:8D:A8")
|
||||
nmc.pexp.expect("get-config: starting")
|
||||
nmc.pexp.expect("get-config: success")
|
||||
nmc.pexp.expect("meta data received")
|
||||
# No changes this time
|
||||
nmc.pexp.expect('device needs no update to applied connection "con-eth0"')
|
||||
nmc.pexp.expect("no changes were applied for provider azure")
|
||||
nmc.pexp.expect(pexpect.EOF)
|
||||
|
||||
Util.valgrind_check_log(nmc.valgrind_log, "test_azure")
|
||||
|
||||
@cloud_setup_test
|
||||
def test_ec2(self):
|
||||
self._mock_devices()
|
||||
|
||||
_ec2_macs = "/2018-09-24/meta-data/network/interfaces/macs/"
|
||||
self._mock_path("/latest/meta-data/", "ami-id\n")
|
||||
self._mock_path(
|
||||
_ec2_macs, TestNmCloudSetup._mac2 + "\n" + TestNmCloudSetup._mac1
|
||||
)
|
||||
self._mock_path(
|
||||
_ec2_macs + TestNmCloudSetup._mac2 + "/subnet-ipv4-cidr-block",
|
||||
"172.31.16.0/20",
|
||||
)
|
||||
self._mock_path(
|
||||
_ec2_macs + TestNmCloudSetup._mac2 + "/local-ipv4s", TestNmCloudSetup._ip1
|
||||
)
|
||||
self._mock_path(
|
||||
_ec2_macs + TestNmCloudSetup._mac1 + "/subnet-ipv4-cidr-block",
|
||||
"172.31.166.0/20",
|
||||
)
|
||||
self._mock_path(
|
||||
_ec2_macs + TestNmCloudSetup._mac1 + "/local-ipv4s", TestNmCloudSetup._ip2
|
||||
)
|
||||
|
||||
# Run nm-cloud-setup for the first time
|
||||
nmc = self.call_pexpect(
|
||||
ENV_NM_TEST_CLIENT_CLOUD_SETUP_PATH,
|
||||
@@ -2251,6 +2478,67 @@ class TestNmCloudSetup(TestNmClient):
|
||||
|
||||
Util.valgrind_check_log(nmc.valgrind_log, "test_ec2")
|
||||
|
||||
@cloud_setup_test
|
||||
def test_gcp(self):
|
||||
self._mock_devices()
|
||||
|
||||
gcp_meta = "/computeMetadata/v1/instance/"
|
||||
gcp_iface = gcp_meta + "network-interfaces/"
|
||||
self._mock_path(gcp_meta + "id", "")
|
||||
self._mock_path(gcp_iface, "0\n1\n")
|
||||
self._mock_path(gcp_iface + "0/mac", TestNmCloudSetup._mac1)
|
||||
self._mock_path(gcp_iface + "1/mac", TestNmCloudSetup._mac2)
|
||||
self._mock_path(gcp_iface + "0/forwarded-ips/", "0\n")
|
||||
self._mock_path(gcp_iface + "0/forwarded-ips/0", TestNmCloudSetup._ip1)
|
||||
self._mock_path(gcp_iface + "1/forwarded-ips/", "0\n")
|
||||
self._mock_path(gcp_iface + "1/forwarded-ips/0", TestNmCloudSetup._ip2)
|
||||
|
||||
# Run nm-cloud-setup for the first time
|
||||
nmc = self.call_pexpect(
|
||||
ENV_NM_TEST_CLIENT_CLOUD_SETUP_PATH,
|
||||
[],
|
||||
{
|
||||
"NM_CLOUD_SETUP_GCP_HOST": self.md_url,
|
||||
"NM_CLOUD_SETUP_LOG": "trace",
|
||||
"NM_CLOUD_SETUP_GCP": "yes",
|
||||
},
|
||||
)
|
||||
|
||||
nmc.pexp.expect("provider GCP detected")
|
||||
nmc.pexp.expect("found interfaces: 9E:C0:3E:92:24:2D, 53:E9:7E:52:8D:A8")
|
||||
nmc.pexp.expect("found GCP interfaces: 2")
|
||||
nmc.pexp.expect("GCP interface\[0]: found a requested device with hwaddr")
|
||||
nmc.pexp.expect("get-config: success")
|
||||
nmc.pexp.expect("meta data received")
|
||||
# One of the devices has no IPv4 configuration to be modified
|
||||
nmc.pexp.expect("device has no suitable applied connection. Skip")
|
||||
# The other one was lacking an address set it up.
|
||||
nmc.pexp.expect("some changes were applied for provider GCP")
|
||||
nmc.pexp.expect(pexpect.EOF)
|
||||
|
||||
# Run nm-cloud-setup for the second time
|
||||
nmc = self.call_pexpect(
|
||||
ENV_NM_TEST_CLIENT_CLOUD_SETUP_PATH,
|
||||
[],
|
||||
{
|
||||
"NM_CLOUD_SETUP_GCP_HOST": self.md_url,
|
||||
"NM_CLOUD_SETUP_LOG": "trace",
|
||||
"NM_CLOUD_SETUP_GCP": "yes",
|
||||
},
|
||||
)
|
||||
|
||||
nmc.pexp.expect("provider GCP detected")
|
||||
nmc.pexp.expect("found interfaces: 9E:C0:3E:92:24:2D, 53:E9:7E:52:8D:A8")
|
||||
nmc.pexp.expect("get-config: starting")
|
||||
nmc.pexp.expect("get-config: success")
|
||||
nmc.pexp.expect("meta data received")
|
||||
# No changes this time
|
||||
nmc.pexp.expect('device needs no update to applied connection "con-eth0"')
|
||||
nmc.pexp.expect("no changes were applied for provider GCP")
|
||||
nmc.pexp.expect(pexpect.EOF)
|
||||
|
||||
Util.valgrind_check_log(nmc.valgrind_log, "test_gcp")
|
||||
|
||||
|
||||
###############################################################################
|
||||
|
||||
|
@@ -1,13 +1,23 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
# A service that mocks up various metadata providers. Used for testing,
|
||||
# can also be used standalone as a development aid.
|
||||
#
|
||||
# To run standalone:
|
||||
#
|
||||
# run: $ systemd-socket-activate -l 8000 python tools/test-cloud-meta-mock.py &
|
||||
# $ NM_CLOUD_SETUP_EC2_HOST=http://localhost:8000 \
|
||||
# NM_CLOUD_SETUP_LOG=trace \
|
||||
# NM_CLOUD_SETUP_EC2=yes src/nm-cloud-setup/nm-cloud-setup
|
||||
# or just: $ python tools/test-cloud-meta-mock.py
|
||||
#
|
||||
# By default, the utility will server some resources for each known cloud
|
||||
# providers, for convenience. The tests start this with "--empty" argument,
|
||||
# which starts with no resources.
|
||||
|
||||
import os
|
||||
import socket
|
||||
from sys import argv
|
||||
|
||||
from http.server import HTTPServer
|
||||
from http.server import BaseHTTPRequestHandler
|
||||
@@ -20,35 +30,39 @@ class MockCloudMDRequestHandler(BaseHTTPRequestHandler):
|
||||
Currently implements a fairly minimal subset of AWS EC2 API.
|
||||
"""
|
||||
|
||||
_ec2_macs = "/2018-09-24/meta-data/network/interfaces/macs/"
|
||||
_meta_resources = {
|
||||
"/latest/meta-data/": b"ami-id\n",
|
||||
_ec2_macs: b"9e:c0:3e:92:24:2d\n53:e9:7e:52:8d:a8",
|
||||
_ec2_macs + "9e:c0:3e:92:24:2d/subnet-ipv4-cidr-block": b"172.31.16.0/20",
|
||||
_ec2_macs + "9e:c0:3e:92:24:2d/local-ipv4s": b"172.31.26.249",
|
||||
_ec2_macs + "53:e9:7e:52:8d:a8/subnet-ipv4-cidr-block": b"172.31.166.0/20",
|
||||
_ec2_macs + "53:e9:7e:52:8d:a8/local-ipv4s": b"172.31.176.249",
|
||||
}
|
||||
|
||||
def log_message(self, format, *args):
|
||||
pass
|
||||
|
||||
def do_GET(self):
|
||||
if self.path in self._meta_resources:
|
||||
path = self.path.encode("ascii")
|
||||
if path in self.server._resources:
|
||||
self.send_response(200)
|
||||
self.end_headers()
|
||||
self.wfile.write(self._meta_resources[self.path])
|
||||
self.wfile.write(self.server._resources[path])
|
||||
else:
|
||||
self.send_response(404)
|
||||
self.end_headers()
|
||||
|
||||
def do_PUT(self):
|
||||
if self.path == "/latest/api/token":
|
||||
path = self.path.encode("ascii")
|
||||
if path == b"/latest/api/token":
|
||||
self.send_response(200)
|
||||
self.end_headers()
|
||||
self.wfile.write(
|
||||
b"AQAAALH-k7i18JMkK-ORLZQfAa7nkNjQbKwpQPExNHqzk1oL_7eh-A=="
|
||||
)
|
||||
else:
|
||||
length = int(self.headers["content-length"])
|
||||
self.server._resources[path] = self.rfile.read(length)
|
||||
self.send_response(201)
|
||||
self.end_headers()
|
||||
|
||||
def do_DELETE(self):
|
||||
path = self.path.encode("ascii")
|
||||
if path in self.server._resources:
|
||||
del self.server._resources[path]
|
||||
self.send_response(204)
|
||||
self.end_headers()
|
||||
else:
|
||||
self.send_response(404)
|
||||
self.end_headers()
|
||||
@@ -61,16 +75,89 @@ class SocketHTTPServer(HTTPServer):
|
||||
fron the test runner.
|
||||
"""
|
||||
|
||||
def __init__(self, server_address, RequestHandlerClass, socket):
|
||||
def __init__(self, server_address, RequestHandlerClass, socket, resources):
|
||||
BaseServer.__init__(self, server_address, RequestHandlerClass)
|
||||
self.socket = socket
|
||||
self.server_address = self.socket.getsockname()
|
||||
self._resources = resources
|
||||
|
||||
|
||||
def default_resources():
|
||||
ec2_macs = b"/2018-09-24/meta-data/network/interfaces/macs/"
|
||||
|
||||
aliyun_meta = b"/2016-01-01/meta-data/"
|
||||
aliyun_macs = aliyun_meta + b"network/interfaces/macs/"
|
||||
|
||||
azure_meta = b"/metadata/instance"
|
||||
azure_iface = azure_meta + b"/network/interface/"
|
||||
azure_query = b"?format=text&api-version=2017-04-02"
|
||||
|
||||
gcp_meta = b"/computeMetadata/v1/instance/"
|
||||
gcp_iface = gcp_meta + b"network-interfaces/"
|
||||
|
||||
mac1 = b"9e:c0:3e:92:24:2d"
|
||||
mac2 = b"53:e9:7e:52:8d:a8"
|
||||
|
||||
ip1 = b"172.31.26.249"
|
||||
ip2 = b"172.31.176.249"
|
||||
|
||||
return {
|
||||
b"/latest/meta-data/": b"ami-id\n",
|
||||
ec2_macs: mac2 + b"\n" + mac1,
|
||||
ec2_macs + mac2 + b"/subnet-ipv4-cidr-block": b"172.31.16.0/20",
|
||||
ec2_macs + mac2 + b"/local-ipv4s": ip1,
|
||||
ec2_macs + mac1 + b"/subnet-ipv4-cidr-block": b"172.31.166.0/20",
|
||||
ec2_macs + mac1 + b"/local-ipv4s": ip2,
|
||||
aliyun_meta: b"ami-id\n",
|
||||
aliyun_macs: mac2 + b"\n" + mac1,
|
||||
aliyun_macs + mac2 + b"/vpc-cidr-block": b"172.31.16.0/20",
|
||||
aliyun_macs + mac2 + b"/private-ipv4s": ip1,
|
||||
aliyun_macs + mac2 + b"/primary-ip-address": ip1,
|
||||
aliyun_macs + mac2 + b"/netmask": b"255.255.255.0",
|
||||
aliyun_macs + mac2 + b"/gateway": b"172.31.26.2",
|
||||
aliyun_macs + mac1 + b"/vpc-cidr-block": b"172.31.166.0/20",
|
||||
aliyun_macs + mac1 + b"/private-ipv4s": ip2,
|
||||
aliyun_macs + mac1 + b"/primary-ip-address": ip2,
|
||||
aliyun_macs + mac1 + b"/netmask": b"255.255.255.0",
|
||||
aliyun_macs + mac1 + b"/gateway": b"172.31.176.2",
|
||||
azure_meta + azure_query: b"",
|
||||
azure_iface + azure_query: b"0\n1\n",
|
||||
azure_iface + b"0/macAddress" + azure_query: mac1,
|
||||
azure_iface + b"1/macAddress" + azure_query: mac2,
|
||||
azure_iface + b"0/ipv4/ipAddress/" + azure_query: b"0\n",
|
||||
azure_iface + b"1/ipv4/ipAddress/" + azure_query: b"0\n",
|
||||
azure_iface + b"0/ipv4/ipAddress/0/privateIpAddress" + azure_query: ip1,
|
||||
azure_iface + b"1/ipv4/ipAddress/0/privateIpAddress" + azure_query: ip2,
|
||||
azure_iface + b"0/ipv4/subnet/0/address/" + azure_query: b"172.31.16.0",
|
||||
azure_iface + b"1/ipv4/subnet/0/address/" + azure_query: b"172.31.166.0",
|
||||
azure_iface + b"0/ipv4/subnet/0/prefix/" + azure_query: b"20",
|
||||
azure_iface + b"1/ipv4/subnet/0/prefix/" + azure_query: b"20",
|
||||
gcp_meta + b"id": b"",
|
||||
gcp_iface: b"0\n1\n",
|
||||
gcp_iface + b"0/mac": mac1,
|
||||
gcp_iface + b"1/mac": mac2,
|
||||
gcp_iface + b"0/forwarded-ips/": b"0\n",
|
||||
gcp_iface + b"0/forwarded-ips/0": ip1,
|
||||
gcp_iface + b"1/forwarded-ips/": b"0\n",
|
||||
gcp_iface + b"1/forwarded-ips/0": ip2,
|
||||
}
|
||||
|
||||
|
||||
resources = None
|
||||
try:
|
||||
if argv[1] == "--empty":
|
||||
resources = {}
|
||||
except IndexError:
|
||||
pass
|
||||
if resources is None:
|
||||
resources = default_resources()
|
||||
|
||||
# See sd_listen_fds(3)
|
||||
fileno = os.getenv("LISTEN_FD")
|
||||
fileno = os.getenv("LISTEN_FDS")
|
||||
if fileno is not None:
|
||||
s = socket.socket(fileno=int(fileno))
|
||||
if fileno != "1":
|
||||
raise Exception("Bad LISTEN_FDS")
|
||||
s = socket.socket(fileno=3)
|
||||
else:
|
||||
addr = ("localhost", 0)
|
||||
s = socket.socket()
|
||||
@@ -78,8 +165,7 @@ else:
|
||||
s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEPORT, 1)
|
||||
s.bind(addr)
|
||||
|
||||
|
||||
httpd = SocketHTTPServer(None, MockCloudMDRequestHandler, socket=s)
|
||||
httpd = SocketHTTPServer(None, MockCloudMDRequestHandler, socket=s, resources=resources)
|
||||
|
||||
print("Listening on http://%s:%d" % (httpd.server_address[0], httpd.server_address[1]))
|
||||
httpd.server_activate()
|
||||
|
Reference in New Issue
Block a user