mirror of
https://gitlab.freedesktop.org/NetworkManager/NetworkManager.git
synced 2026-05-05 09:58:07 +02:00
cloud-setup: merge branch 'th/cloud-setup-testable'
https://gitlab.freedesktop.org/NetworkManager/NetworkManager/-/merge_requests/1632
This commit is contained in:
commit
1b26315a1f
8 changed files with 380 additions and 209 deletions
|
|
@ -27,38 +27,35 @@ _provider_detect_cb(GObject *source, GAsyncResult *result, gpointer user_data)
|
|||
{
|
||||
gs_unref_object NMCSProvider *provider = NMCS_PROVIDER(source);
|
||||
gs_free_error GError *error = NULL;
|
||||
ProviderDetectData *dd;
|
||||
ProviderDetectData *dd = user_data;
|
||||
gboolean success;
|
||||
|
||||
nm_assert(dd->detect_count > 0);
|
||||
dd->detect_count--;
|
||||
|
||||
success = nmcs_provider_detect_finish(provider, result, &error);
|
||||
|
||||
nm_assert(success != (!!error));
|
||||
|
||||
if (nm_utils_error_is_cancelled(error))
|
||||
return;
|
||||
|
||||
dd = user_data;
|
||||
|
||||
nm_assert(dd->detect_count > 0);
|
||||
dd->detect_count--;
|
||||
|
||||
if (nm_utils_error_is_cancelled(error)) {
|
||||
_LOGD("provider %s detection cancelled", nmcs_provider_get_name(provider));
|
||||
goto out;
|
||||
}
|
||||
if (error) {
|
||||
_LOGI("provider %s not detected: %s", nmcs_provider_get_name(provider), error->message);
|
||||
if (dd->detect_count > 0) {
|
||||
/* wait longer. */
|
||||
return;
|
||||
}
|
||||
|
||||
_LOGI("no provider detected");
|
||||
goto done;
|
||||
goto out;
|
||||
}
|
||||
|
||||
_LOGI("provider %s detected", nmcs_provider_get_name(provider));
|
||||
dd->provider_result = g_steal_pointer(&provider);
|
||||
|
||||
done:
|
||||
g_cancellable_cancel(dd->cancellable);
|
||||
g_main_loop_quit(dd->main_loop);
|
||||
|
||||
out:
|
||||
if (dd->detect_count == 0) {
|
||||
if (!dd->provider_result)
|
||||
_LOGI("no provider detected");
|
||||
g_main_loop_quit(dd->main_loop);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
|
|
@ -68,8 +65,6 @@ _provider_detect_sigterm_cb(GCancellable *source, gpointer user_data)
|
|||
|
||||
g_cancellable_cancel(dd->cancellable);
|
||||
g_clear_object(&dd->provider_result);
|
||||
dd->detect_count = 0;
|
||||
g_main_loop_quit(dd->main_loop);
|
||||
}
|
||||
|
||||
static NMCSProvider *
|
||||
|
|
@ -123,6 +118,107 @@ out:
|
|||
|
||||
/*****************************************************************************/
|
||||
|
||||
static NMUtilsNamedValue *
|
||||
_map_interfaces_parse(void)
|
||||
{
|
||||
gs_free const char **split = NULL;
|
||||
NMUtilsNamedValue *map_interfaces;
|
||||
const char *env_var;
|
||||
gsize i;
|
||||
gsize j;
|
||||
gsize alloc_len;
|
||||
|
||||
env_var = g_getenv(NMCS_ENV_NM_CLOUD_SETUP_MAP_INTERFACES);
|
||||
|
||||
if (nm_str_is_empty(env_var))
|
||||
return NULL;
|
||||
|
||||
split = nm_strsplit_set_full(env_var, ";", NM_STRSPLIT_SET_FLAGS_STRSTRIP);
|
||||
|
||||
alloc_len = NM_PTRARRAY_LEN(split) + 1u;
|
||||
|
||||
map_interfaces = g_new(NMUtilsNamedValue, alloc_len);
|
||||
|
||||
_LOGD("test: map interfaces via NM_CLOUD_SETUP_MAP_INTERFACES=\"%s\"", env_var);
|
||||
|
||||
for (i = 0, j = 0; split && split[i]; i++) {
|
||||
NMUtilsNamedValue *m;
|
||||
const char *str = split[i];
|
||||
char *hwaddr;
|
||||
const char *s;
|
||||
|
||||
s = strchr(str, '=');
|
||||
if (!s || str == s)
|
||||
continue;
|
||||
|
||||
hwaddr = nmcs_utils_hwaddr_normalize(&s[1], -1);
|
||||
if (!hwaddr)
|
||||
continue;
|
||||
|
||||
nm_assert(j < alloc_len);
|
||||
m = &map_interfaces[j++];
|
||||
|
||||
*m = (NMUtilsNamedValue){
|
||||
.name = g_strndup(str, s - str),
|
||||
.value_str = hwaddr,
|
||||
};
|
||||
|
||||
_LOGD("test: map \"%s\" -> %s", m->name, m->value_str);
|
||||
}
|
||||
|
||||
nm_assert(j < alloc_len);
|
||||
map_interfaces[j++] = (NMUtilsNamedValue){
|
||||
.name = NULL,
|
||||
.value_str = NULL,
|
||||
};
|
||||
|
||||
return g_steal_pointer(&map_interfaces);
|
||||
}
|
||||
|
||||
static const char *
|
||||
_device_get_hwaddr(NMDeviceEthernet *device)
|
||||
{
|
||||
static const NMUtilsNamedValue *gl_map_interfaces_map = NULL;
|
||||
static gsize gl_initialized = 0;
|
||||
const NMUtilsNamedValue *map = NULL;
|
||||
|
||||
nm_assert(NM_IS_DEVICE_ETHERNET(device));
|
||||
|
||||
/* Network interfaces in cloud environments are identified by their permanent
|
||||
* MAC address.
|
||||
*
|
||||
* For testing, we can set NMCS_ENV_NM_CLOUD_SETUP_MAP_INTERFACES
|
||||
* to a ';' separate list of "$INTERFACE=$HWADDR", which means that we
|
||||
* pretend that device with ip-interface "$INTERFACE" has the specified permanent
|
||||
* MAC address. */
|
||||
|
||||
if (g_once_init_enter(&gl_initialized)) {
|
||||
gl_map_interfaces_map = _map_interfaces_parse();
|
||||
g_once_init_leave(&gl_initialized, 1);
|
||||
}
|
||||
|
||||
map = gl_map_interfaces_map;
|
||||
if (G_UNLIKELY(map)) {
|
||||
const char *const iface = nm_device_get_iface(NM_DEVICE(device));
|
||||
|
||||
/* For testing, the device<->hwaddr is remapped and the actual permanent
|
||||
* MAC address of the device ignored. This mapping is configured via
|
||||
* NMCS_ENV_NM_CLOUD_SETUP_MAP_INTERFACES environment variable. */
|
||||
|
||||
if (!iface)
|
||||
return NULL;
|
||||
|
||||
for (; map->name; map++) {
|
||||
if (nm_streq(map->name, iface))
|
||||
return map->value_str;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return nm_device_ethernet_get_permanent_hw_address(device);
|
||||
}
|
||||
|
||||
static char **
|
||||
_nmc_get_hwaddrs(NMClient *nmc)
|
||||
{
|
||||
|
|
@ -145,7 +241,7 @@ _nmc_get_hwaddrs(NMClient *nmc)
|
|||
if (nm_device_get_state(device) < NM_DEVICE_STATE_UNAVAILABLE)
|
||||
continue;
|
||||
|
||||
hwaddr = nm_device_ethernet_get_permanent_hw_address(NM_DEVICE_ETHERNET(device));
|
||||
hwaddr = _device_get_hwaddr(NM_DEVICE_ETHERNET(device));
|
||||
if (!hwaddr)
|
||||
continue;
|
||||
|
||||
|
|
@ -187,7 +283,7 @@ _nmc_get_device_by_hwaddr(NMClient *nmc, const char *hwaddr)
|
|||
if (!NM_IS_DEVICE_ETHERNET(device))
|
||||
continue;
|
||||
|
||||
hwaddr_dev = nm_device_ethernet_get_permanent_hw_address(NM_DEVICE_ETHERNET(device));
|
||||
hwaddr_dev = _device_get_hwaddr(NM_DEVICE_ETHERNET(device));
|
||||
if (!hwaddr_dev)
|
||||
continue;
|
||||
|
||||
|
|
@ -633,7 +729,7 @@ main(int argc, const char *const *argv)
|
|||
nm_auto_free_nmcs_provider_get_config_result NMCSProviderGetConfigResult *result = NULL;
|
||||
gs_free_error GError *error = NULL;
|
||||
|
||||
_nm_logging_enabled_init(g_getenv(NMCS_ENV_VARIABLE("NM_CLOUD_SETUP_LOG")));
|
||||
_nm_logging_enabled_init(g_getenv(NMCS_ENV_NM_CLOUD_SETUP_LOG));
|
||||
|
||||
_LOGD("nm-cloud-setup %s starting...", NM_DIST_VERSION);
|
||||
|
||||
|
|
|
|||
|
|
@ -7,9 +7,20 @@
|
|||
|
||||
/*****************************************************************************/
|
||||
|
||||
/* mark names for variables that can be used as configuration. Search
|
||||
* for NMCS_ENV_VARIABLE() to find all honored environment variables. */
|
||||
#define NMCS_ENV_VARIABLE(var) "" var ""
|
||||
/* Environment variables for configuring nm-cloud-setup */
|
||||
#define NMCS_ENV_NM_CLOUD_SETUP_ALIYUN "NM_CLOUD_SETUP_ALIYUN"
|
||||
#define NMCS_ENV_NM_CLOUD_SETUP_AZURE "NM_CLOUD_SETUP_AZURE"
|
||||
#define NMCS_ENV_NM_CLOUD_SETUP_EC2 "NM_CLOUD_SETUP_EC2"
|
||||
#define NMCS_ENV_NM_CLOUD_SETUP_GCP "NM_CLOUD_SETUP_GCP"
|
||||
#define NMCS_ENV_NM_CLOUD_SETUP_LOG "NM_CLOUD_SETUP_LOG"
|
||||
|
||||
/* Undocumented/internal environment variables for configuring nm-cloud-setup.
|
||||
* These are mainly for testing/debugging. */
|
||||
#define NMCS_ENV_NM_CLOUD_SETUP_ALIYUN_HOST "NM_CLOUD_SETUP_ALIYUN_HOST"
|
||||
#define NMCS_ENV_NM_CLOUD_SETUP_AZURE_HOST "NM_CLOUD_SETUP_AZURE_HOST"
|
||||
#define NMCS_ENV_NM_CLOUD_SETUP_EC2_HOST "NM_CLOUD_SETUP_EC2_HOST"
|
||||
#define NMCS_ENV_NM_CLOUD_SETUP_GCP_HOST "NM_CLOUD_SETUP_GCP_HOST"
|
||||
#define NMCS_ENV_NM_CLOUD_SETUP_MAP_INTERFACES "NM_CLOUD_SETUP_MAP_INTERFACES"
|
||||
|
||||
/*****************************************************************************/
|
||||
|
||||
|
|
@ -80,6 +91,38 @@ const char *nmcs_utils_parse_get_full_line(GBytes *mem, const char *needle);
|
|||
|
||||
/*****************************************************************************/
|
||||
|
||||
#define NMCS_DEFINE_HOST_BASE(base_fcn, nmcs_env_host, default_host) \
|
||||
static const char *base_fcn(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_host ""); \
|
||||
base = nmcs_utils_uri_complete_interned(base) ?: ("" default_host ""); \
|
||||
\
|
||||
if (!g_atomic_pointer_compare_and_exchange(&base_cached, NULL, base)) \
|
||||
goto again; \
|
||||
\
|
||||
if (!nm_streq(base, ("" default_host ""))) { \
|
||||
_LOGD("test: mock %s=\"%s\" (default \"%s\")", \
|
||||
"" nmcs_env_host "", \
|
||||
base, \
|
||||
"" default_host ""); \
|
||||
} \
|
||||
} \
|
||||
\
|
||||
return base; \
|
||||
} \
|
||||
_NM_DUMMY_STRUCT_FOR_TRAILING_SEMICOLON
|
||||
|
||||
/*****************************************************************************/
|
||||
|
||||
char *nmcs_utils_uri_build_concat_v(const char *base, const char **components, gsize n_components);
|
||||
|
||||
#define nmcs_utils_uri_build_concat(base, ...) \
|
||||
|
|
|
|||
|
|
@ -18,26 +18,7 @@
|
|||
#define NM_ALIYUN_METADATA_URL_BASE /* $NM_ALIYUN_BASE/$NM_ALIYUN_API_VERSION */ \
|
||||
"/meta-data/network/interfaces/macs/"
|
||||
|
||||
static const char *
|
||||
_aliyun_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_ALIYUN_HOST"));
|
||||
|
||||
if (!g_atomic_pointer_compare_and_exchange(&base_cached, NULL, base))
|
||||
goto again;
|
||||
}
|
||||
base = nmcs_utils_uri_complete_interned(base) ?: ("" NM_ALIYUN_BASE);
|
||||
return base;
|
||||
}
|
||||
NMCS_DEFINE_HOST_BASE(_aliyun_base, NMCS_ENV_NM_CLOUD_SETUP_ALIYUN_HOST, NM_ALIYUN_HOST);
|
||||
|
||||
#define _aliyun_uri_concat(...) nmcs_utils_uri_build_concat(_aliyun_base(), __VA_ARGS__)
|
||||
#define _aliyun_uri_interfaces(...) \
|
||||
|
|
@ -557,7 +538,7 @@ nmcs_provider_aliyun_class_init(NMCSProviderAliyunClass *klass)
|
|||
NMCSProviderClass *provider_class = NMCS_PROVIDER_CLASS(klass);
|
||||
|
||||
provider_class->_name = "aliyun";
|
||||
provider_class->_env_provider_enabled = NMCS_ENV_VARIABLE("NM_CLOUD_SETUP_ALIYUN");
|
||||
provider_class->_env_provider_enabled = NMCS_ENV_NM_CLOUD_SETUP_ALIYUN;
|
||||
provider_class->detect = detect;
|
||||
provider_class->get_config = get_config;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,27 +17,7 @@
|
|||
#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;
|
||||
}
|
||||
NMCS_DEFINE_HOST_BASE(_azure_base, NMCS_ENV_NM_CLOUD_SETUP_AZURE_HOST, NM_AZURE_BASE);
|
||||
|
||||
#define _azure_uri_concat(...) \
|
||||
nmcs_utils_uri_build_concat(_azure_base(), __VA_ARGS__, NM_AZURE_API_VERSION)
|
||||
|
|
@ -586,7 +566,7 @@ nmcs_provider_azure_class_init(NMCSProviderAzureClass *klass)
|
|||
NMCSProviderClass *provider_class = NMCS_PROVIDER_CLASS(klass);
|
||||
|
||||
provider_class->_name = "azure";
|
||||
provider_class->_env_provider_enabled = NMCS_ENV_VARIABLE("NM_CLOUD_SETUP_AZURE");
|
||||
provider_class->_env_provider_enabled = NMCS_ENV_NM_CLOUD_SETUP_AZURE;
|
||||
provider_class->detect = detect;
|
||||
provider_class->get_config = get_config;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -21,27 +21,7 @@
|
|||
#define NM_EC2_TOKEN_TTL_HEADER "X-aws-ec2-metadata-token-ttl-seconds: 180"
|
||||
#define NM_EC2_TOKEN_HEADER "X-aws-ec2-metadata-token: "
|
||||
|
||||
static const char *
|
||||
_ec2_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_EC2_HOST"));
|
||||
base = nmcs_utils_uri_complete_interned(base) ?: ("" NM_EC2_BASE);
|
||||
|
||||
if (!g_atomic_pointer_compare_and_exchange(&base_cached, NULL, base))
|
||||
goto again;
|
||||
}
|
||||
|
||||
return base;
|
||||
}
|
||||
NMCS_DEFINE_HOST_BASE(_ec2_base, NMCS_ENV_NM_CLOUD_SETUP_EC2_HOST, NM_EC2_BASE);
|
||||
|
||||
#define _ec2_uri_concat(...) nmcs_utils_uri_build_concat(_ec2_base(), __VA_ARGS__)
|
||||
#define _ec2_uri_interfaces(...) \
|
||||
|
|
@ -435,7 +415,7 @@ nmcs_provider_ec2_class_init(NMCSProviderEC2Class *klass)
|
|||
object_class->dispose = dispose;
|
||||
|
||||
provider_class->_name = "ec2";
|
||||
provider_class->_env_provider_enabled = NMCS_ENV_VARIABLE("NM_CLOUD_SETUP_EC2");
|
||||
provider_class->_env_provider_enabled = NMCS_ENV_NM_CLOUD_SETUP_EC2;
|
||||
provider_class->detect = detect;
|
||||
provider_class->get_config = get_config;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -20,27 +20,7 @@
|
|||
|
||||
#define NM_GCP_METADATA_HEADER "Metadata-Flavor: Google"
|
||||
|
||||
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;
|
||||
}
|
||||
NMCS_DEFINE_HOST_BASE(_gcp_base, NMCS_ENV_NM_CLOUD_SETUP_GCP_HOST, NM_GCP_BASE);
|
||||
|
||||
#define _gcp_uri_concat(...) \
|
||||
nmcs_utils_uri_build_concat(_gcp_base(), \
|
||||
|
|
@ -482,7 +462,7 @@ nmcs_provider_gcp_class_init(NMCSProviderGCPClass *klass)
|
|||
NMCSProviderClass *provider_class = NMCS_PROVIDER_CLASS(klass);
|
||||
|
||||
provider_class->_name = "GCP";
|
||||
provider_class->_env_provider_enabled = NMCS_ENV_VARIABLE("NM_CLOUD_SETUP_GCP");
|
||||
provider_class->_env_provider_enabled = NMCS_ENV_NM_CLOUD_SETUP_GCP;
|
||||
provider_class->detect = detect;
|
||||
provider_class->get_config = get_config;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2203,8 +2203,8 @@ class TestNmCloudSetup(unittest.TestCase):
|
|||
Util.skip_without_NM()
|
||||
self.ctx = NMTestContext(self._testMethodName)
|
||||
|
||||
_mac1 = "9e:c0:3e:92:24:2d"
|
||||
_mac2 = "53:e9:7e:52:8d:a8"
|
||||
_mac1 = "cc:00:00:00:00:01"
|
||||
_mac2 = "cc:00:00:00:00:02"
|
||||
|
||||
_ip1 = "172.31.26.249"
|
||||
_ip2 = "172.31.176.249"
|
||||
|
|
@ -2271,7 +2271,7 @@ class TestNmCloudSetup(unittest.TestCase):
|
|||
|
||||
def _mock_devices(self):
|
||||
# Add a device with an active connection that has IPv4 configured
|
||||
self.ctx.srv.op_AddObj("WiredDevice", iface="eth0", mac="9e:c0:3e:92:24:2d")
|
||||
self.ctx.srv.op_AddObj("WiredDevice", iface="eth0", mac="cc:00:00:00:00:01")
|
||||
self.ctx.srv.addAndActivateConnection(
|
||||
{
|
||||
"connection": {"type": "802-3-ethernet", "id": "con-eth0"},
|
||||
|
|
@ -2282,7 +2282,7 @@ class TestNmCloudSetup(unittest.TestCase):
|
|||
)
|
||||
|
||||
# The second connection has no IPv4
|
||||
self.ctx.srv.op_AddObj("WiredDevice", iface="eth1", mac="53:e9:7e:52:8d:a8")
|
||||
self.ctx.srv.op_AddObj("WiredDevice", iface="eth1", mac="cc:00:00:00:00:02")
|
||||
self.ctx.srv.addAndActivateConnection(
|
||||
{"connection": {"type": "802-3-ethernet", "id": "con-eth1"}},
|
||||
"/org/freedesktop/NetworkManager/Devices/2",
|
||||
|
|
@ -2351,7 +2351,7 @@ class TestNmCloudSetup(unittest.TestCase):
|
|||
)
|
||||
|
||||
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("found interfaces: CC:00:00:00:00:01, CC:00:00:00:00:02")
|
||||
nmc.pexp.expect("get-config: start fetching meta data")
|
||||
nmc.pexp.expect("get-config: success")
|
||||
nmc.pexp.expect("meta data received")
|
||||
|
|
@ -2373,7 +2373,7 @@ class TestNmCloudSetup(unittest.TestCase):
|
|||
)
|
||||
|
||||
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("found interfaces: CC:00:00:00:00:01, CC:00:00:00:00:02")
|
||||
nmc.pexp.expect("get-config: starting")
|
||||
nmc.pexp.expect("get-config: success")
|
||||
nmc.pexp.expect("meta data received")
|
||||
|
|
@ -2430,7 +2430,7 @@ class TestNmCloudSetup(unittest.TestCase):
|
|||
)
|
||||
|
||||
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 interfaces: CC:00:00:00:00:01, CC:00:00:00:00:02")
|
||||
nmc.pexp.expect("found azure interfaces: 2")
|
||||
nmc.pexp.expect("interface\[0]: found a matching device with hwaddr")
|
||||
nmc.pexp.expect(
|
||||
|
|
@ -2459,7 +2459,7 @@ class TestNmCloudSetup(unittest.TestCase):
|
|||
)
|
||||
|
||||
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 interfaces: CC:00:00:00:00:01, CC:00:00:00:00:02")
|
||||
nmc.pexp.expect("get-config: starting")
|
||||
nmc.pexp.expect("get-config: success")
|
||||
nmc.pexp.expect("meta data received")
|
||||
|
|
@ -2506,7 +2506,7 @@ class TestNmCloudSetup(unittest.TestCase):
|
|||
)
|
||||
|
||||
nmc.pexp.expect("provider ec2 detected")
|
||||
nmc.pexp.expect("found interfaces: 9E:C0:3E:92:24:2D, 53:E9:7E:52:8D:A8")
|
||||
nmc.pexp.expect("found interfaces: CC:00:00:00:00:01, CC:00:00:00:00:02")
|
||||
nmc.pexp.expect("get-config: starting")
|
||||
nmc.pexp.expect("get-config: success")
|
||||
nmc.pexp.expect("meta data received")
|
||||
|
|
@ -2528,7 +2528,7 @@ class TestNmCloudSetup(unittest.TestCase):
|
|||
)
|
||||
|
||||
nmc.pexp.expect("provider ec2 detected")
|
||||
nmc.pexp.expect("found interfaces: 9E:C0:3E:92:24:2D, 53:E9:7E:52:8D:A8")
|
||||
nmc.pexp.expect("found interfaces: CC:00:00:00:00:01, CC:00:00:00:00:02")
|
||||
nmc.pexp.expect("get-config: starting")
|
||||
nmc.pexp.expect("get-config: success")
|
||||
nmc.pexp.expect("meta data received")
|
||||
|
|
@ -2566,7 +2566,7 @@ class TestNmCloudSetup(unittest.TestCase):
|
|||
)
|
||||
|
||||
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 interfaces: CC:00:00:00:00:01, CC:00:00:00:00:02")
|
||||
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")
|
||||
|
|
@ -2589,7 +2589,7 @@ class TestNmCloudSetup(unittest.TestCase):
|
|||
)
|
||||
|
||||
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 interfaces: CC:00:00:00:00:01, CC:00:00:00:00:02")
|
||||
nmc.pexp.expect("get-config: starting")
|
||||
nmc.pexp.expect("get-config: success")
|
||||
nmc.pexp.expect("meta data received")
|
||||
|
|
|
|||
|
|
@ -17,13 +17,45 @@
|
|||
|
||||
import os
|
||||
import socket
|
||||
from sys import argv
|
||||
import sys
|
||||
|
||||
from http.server import HTTPServer
|
||||
from http.server import BaseHTTPRequestHandler
|
||||
from socketserver import BaseServer
|
||||
|
||||
|
||||
PROVIDERS = [
|
||||
"aliyun",
|
||||
"azure",
|
||||
"ec2",
|
||||
"gcp",
|
||||
]
|
||||
|
||||
|
||||
def _s_to_bool(s):
|
||||
s0 = s
|
||||
if isinstance(s, bytes):
|
||||
s = s.encode("utf-8", errors="replace")
|
||||
if isinstance(s, str):
|
||||
s = s.lower()
|
||||
if s in ["yes", "y", "true", "1"]:
|
||||
return True
|
||||
if s in ["no", "n", "false", "0"]:
|
||||
return False
|
||||
if isinstance(s, int):
|
||||
if s in [0, 1]:
|
||||
return s == 1
|
||||
raise ValueError(f'Not a boolean value ("{s0}")')
|
||||
|
||||
|
||||
DEBUG = _s_to_bool(os.environ.get("NM_TEST_CLOUD_SETUP_MOCK_DEBUG", "0"))
|
||||
|
||||
|
||||
def dbg(msg):
|
||||
if DEBUG:
|
||||
print("DBG: %s" % (msg,))
|
||||
|
||||
|
||||
class MockCloudMDRequestHandler(BaseHTTPRequestHandler):
|
||||
"""
|
||||
Respond to cloud metadata service requests.
|
||||
|
|
@ -33,39 +65,72 @@ class MockCloudMDRequestHandler(BaseHTTPRequestHandler):
|
|||
def log_message(self, format, *args):
|
||||
pass
|
||||
|
||||
def _response_and_end(self, code, write=None):
|
||||
self.send_response(code)
|
||||
self.end_headers()
|
||||
if write is None:
|
||||
dbg("response %s" % (code,))
|
||||
else:
|
||||
if isinstance(write, str):
|
||||
write = write.encode("utf-8")
|
||||
dbg("response %s, %s" % (code, write))
|
||||
self.wfile.write(write)
|
||||
|
||||
def _read(self):
|
||||
length = int(self.headers["content-length"])
|
||||
v = self.rfile.read(length)
|
||||
dbg('receive "%s"' % (v,))
|
||||
return v
|
||||
|
||||
def do_GET(self):
|
||||
path = self.path.encode("ascii")
|
||||
dbg("GET %s" % (path,))
|
||||
r = None
|
||||
if path in self.server._resources:
|
||||
self.send_response(200)
|
||||
self.end_headers()
|
||||
self.wfile.write(self.server._resources[path])
|
||||
else:
|
||||
self.send_response(404)
|
||||
self.end_headers()
|
||||
r = self.server._resources[path]
|
||||
elif self.server.config_get_allow_default():
|
||||
for p in self.server.config_get_providers():
|
||||
if path in DEFAULT_RESOURCES[p]:
|
||||
r = DEFAULT_RESOURCES[p][path]
|
||||
break
|
||||
if r is None:
|
||||
self._response_and_end(404)
|
||||
return
|
||||
self._response_and_end(200, write=r)
|
||||
|
||||
def do_PUT(self):
|
||||
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=="
|
||||
)
|
||||
dbg("PUT %s" % (path,))
|
||||
if path.startswith(b"/.nmtest/"):
|
||||
conf_name = path[len(b"/.nmtest/") :]
|
||||
v = self._read()
|
||||
|
||||
self.server._config[conf_name] = v
|
||||
|
||||
assert self.server.config_get_providers() is not None
|
||||
assert self.server.config_get_allow_default() is not None
|
||||
|
||||
self._response_and_end(201)
|
||||
elif path == b"/latest/api/token":
|
||||
if "ec2" not in self.server.config_get_providers():
|
||||
self._response_and_end(404)
|
||||
else:
|
||||
self._response_and_end(
|
||||
200,
|
||||
write="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()
|
||||
self.server._resources[path] = self._read()
|
||||
self._response_and_end(201)
|
||||
|
||||
def do_DELETE(self):
|
||||
path = self.path.encode("ascii")
|
||||
dbg("DELETE %s" % (path,))
|
||||
if path in self.server._resources:
|
||||
del self.server._resources[path]
|
||||
self.send_response(204)
|
||||
self.end_headers()
|
||||
self._response_and_end(204)
|
||||
else:
|
||||
self.send_response(404)
|
||||
self.end_headers()
|
||||
self._response_and_end(404)
|
||||
|
||||
|
||||
class SocketHTTPServer(HTTPServer):
|
||||
|
|
@ -75,82 +140,123 @@ class SocketHTTPServer(HTTPServer):
|
|||
fron the test runner.
|
||||
"""
|
||||
|
||||
def __init__(self, server_address, RequestHandlerClass, socket, resources):
|
||||
def __init__(
|
||||
self,
|
||||
server_address,
|
||||
RequestHandlerClass,
|
||||
socket,
|
||||
resources=None,
|
||||
allow_default=True,
|
||||
):
|
||||
BaseServer.__init__(self, server_address, RequestHandlerClass)
|
||||
self.socket = socket
|
||||
self.server_address = self.socket.getsockname()
|
||||
self._resources = resources
|
||||
self._resources = resources or {}
|
||||
self._config = {
|
||||
"allow-default": "yes" if allow_default else "no",
|
||||
}
|
||||
|
||||
def config_get_providers(self):
|
||||
conf = self._config.get(b"providers", None)
|
||||
if not conf:
|
||||
return PROVIDERS
|
||||
parsed = [s.lower() for s in conf.decode("utf-8", errors="replace").split(" ")]
|
||||
assert all(p in PROVIDERS for p in parsed)
|
||||
return parsed
|
||||
|
||||
def config_get_allow_default(self):
|
||||
return _s_to_bool(self._config.get(b"allow-default", "yes"))
|
||||
|
||||
|
||||
def default_resources():
|
||||
ec2_macs = b"/2018-09-24/meta-data/network/interfaces/macs/"
|
||||
def create_default_resources_for_provider(provider):
|
||||
|
||||
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"
|
||||
mac1 = b"cc:00:00:00:00:01"
|
||||
mac2 = b"cc:00:00:00:00:02"
|
||||
|
||||
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,
|
||||
}
|
||||
if provider == "aliyun":
|
||||
aliyun_meta = b"/2016-01-01/meta-data/"
|
||||
aliyun_macs = aliyun_meta + b"network/interfaces/macs/"
|
||||
return {
|
||||
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",
|
||||
}
|
||||
|
||||
if provider == "azure":
|
||||
azure_meta = b"/metadata/instance"
|
||||
azure_iface = azure_meta + b"/network/interface/"
|
||||
azure_query = b"?format=text&api-version=2017-04-02"
|
||||
return {
|
||||
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",
|
||||
}
|
||||
|
||||
if provider == "ec2":
|
||||
ec2_macs = b"/2018-09-24/meta-data/network/interfaces/macs/"
|
||||
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,
|
||||
},
|
||||
)
|
||||
|
||||
if provider == "gcp":
|
||||
gcp_meta = b"/computeMetadata/v1/instance/"
|
||||
gcp_iface = gcp_meta + b"network-interfaces/"
|
||||
return {
|
||||
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,
|
||||
}
|
||||
|
||||
raise ValueError("invalid provider %s" % (provider,))
|
||||
|
||||
|
||||
resources = None
|
||||
def create_default_resources():
|
||||
|
||||
return {p: create_default_resources_for_provider(p) for p in PROVIDERS}
|
||||
|
||||
|
||||
DEFAULT_RESOURCES = create_default_resources()
|
||||
|
||||
|
||||
allow_default = True
|
||||
try:
|
||||
if argv[1] == "--empty":
|
||||
resources = {}
|
||||
if sys.argv[1] == "--empty":
|
||||
allow_default = False
|
||||
except IndexError:
|
||||
pass
|
||||
if resources is None:
|
||||
resources = default_resources()
|
||||
|
||||
# See sd_listen_fds(3)
|
||||
fileno = os.getenv("LISTEN_FDS")
|
||||
|
|
@ -165,7 +271,12 @@ else:
|
|||
s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEPORT, 1)
|
||||
s.bind(addr)
|
||||
|
||||
httpd = SocketHTTPServer(None, MockCloudMDRequestHandler, socket=s, resources=resources)
|
||||
httpd = SocketHTTPServer(
|
||||
None,
|
||||
MockCloudMDRequestHandler,
|
||||
socket=s,
|
||||
allow_default=allow_default,
|
||||
)
|
||||
|
||||
print("Listening on http://%s:%d" % (httpd.server_address[0], httpd.server_address[1]))
|
||||
httpd.server_activate()
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue