Allow to specify the probed cloud stacks with -c cloud[,cloud...]

This commit is contained in:
reykfloeter 2019-06-04 18:09:50 +02:00
parent ba34eb76dd
commit ca22cba8e4
7 changed files with 210 additions and 85 deletions

View file

@ -63,16 +63,22 @@ azure(struct system_config *sc)
{ {
int ret = -1; int ret = -1;
sc->sc_stack = "azure"; if (sc->sc_state == STATE_INIT) {
free(sc->sc_username);
if ((sc->sc_username = strdup("azure-user")) == NULL) {
log_warnx("failed to set default user");
return (-1);
}
/* Apply defaults */ /* Apply defaults */
free(sc->sc_username); sc->sc_ovfenv = "/var/db/azure-ovf-env.xml";
if ((sc->sc_username = strdup("azure-user")) == NULL) { sc->sc_priv = &az_config;
log_warnx("failed to set default user"); sc->sc_state = STATE_DHCP;
goto fail; return (-1);
} }
sc->sc_ovfenv = "/var/db/azure-ovf-env.xml";
sc->sc_priv = &az_config; /* Don't try other endpoints */
sc->sc_state = STATE_DONE;
if (azure_getovfenv(sc) != 0) { if (azure_getovfenv(sc) != 0) {
log_warnx("failed to get ovf-env.xml"); log_warnx("failed to get ovf-env.xml");

View file

@ -23,6 +23,7 @@
.Sh SYNOPSIS .Sh SYNOPSIS
.Nm cloud-agent .Nm cloud-agent
.Op Fl nuv .Op Fl nuv
.Op Fl c Ar cloud Ns Op , Ns Ar cloud Ns ...
.Op Fl p Ar length .Op Fl p Ar length
.Op Fl r Ar rootdisk .Op Fl r Ar rootdisk
.Op Fl t Ar timeout .Op Fl t Ar timeout
@ -32,10 +33,31 @@
The The
.Nm .Nm
program manages the OpenBSD provisioning and VM interaction in cloud program manages the OpenBSD provisioning and VM interaction in cloud
environments, including Microsoft Azure and Amazon AWS. environments.
.Pp .Pp
The options are as follows: The options are as follows:
.Bl -tag -width Ds .Bl -tag -width Ds
.It Fl c Ar cloud Ns Op , Ns Ar cloud Ns ...
Probe a list of cloud stacks for provisioning in the specified order.
If this option is not specified,
.Nm
tries to detect the environment and possible cloud stacks automatically.
Supported
.Ar cloud
stacks are:
.Pp
.Bl -tag -width opennebula -offset indent -compact
.It Ic azure
Microsoft Azure
.It Ic cloudinit
Generic cloud-init
.It Ic ec2
Amazon AWS EC2
.It Ic opennebula
OpenNebula
.It Ic openstack
OpenStack
.El
.It Fl p Ar length .It Fl p Ar length
Generate and set a random password for the default user. Generate and set a random password for the default user.
The password will be written in its plain form into the The password will be written in its plain form into the

View file

@ -31,65 +31,29 @@
static int cloudinit_fetch(struct system_config *); static int cloudinit_fetch(struct system_config *);
int
tryendpoint(struct system_config *sc,
int (fetch)(struct system_config *),
int (next)(struct system_config *))
{
int errfd = -1, ret;
free(sc->sc_endpoint);
sc->sc_endpoint = NULL;
switch (sc->sc_dhcpendpoint) {
case 0:
sc->sc_dhcpendpoint = 1;
if (dhcp_getendpoint(sc) == -1)
return tryendpoint(sc, fetch, next);
break;
case 1:
sc->sc_dhcpendpoint = 2;
if ((sc->sc_endpoint = strdup(DEFAULT_ENDPOINT)) == NULL) {
log_warnx("failed to set defaults");
return (-1);
}
break;
default:
if (next == NULL)
return (-1);
sc->sc_dhcpendpoint = 0;
return (*next)(sc);
}
errfd = disable_output(sc, STDERR_FILENO);
ret = (*fetch)(sc);
enable_output(sc, STDERR_FILENO, errfd);
if (ret != 0)
return tryendpoint(sc, fetch, next);
return (0);
}
int int
ec2(struct system_config *sc) ec2(struct system_config *sc)
{ {
free(sc->sc_username); if (sc->sc_state == STATE_INIT) {
if ((sc->sc_username = strdup("ec2-user")) == NULL) { free(sc->sc_username);
log_warnx("failed to set default user"); if ((sc->sc_username = strdup("ec2-user")) == NULL) {
log_warnx("failed to set default user");
return (-1);
}
sc->sc_state = STATE_169;
return (-1); return (-1);
} }
return cloudinit_fetch(sc);
sc->sc_stack = "ec2";
sc->sc_dhcpendpoint = 1;
return tryendpoint(sc, cloudinit_fetch, NULL);
} }
int int
cloudinit(struct system_config *sc) cloudinit(struct system_config *sc)
{ {
sc->sc_stack = "cloudinit"; if (sc->sc_state == STATE_INIT) {
sc->sc_dhcpendpoint = 0; sc->sc_state = STATE_DHCP;
return tryendpoint(sc, cloudinit_fetch, NULL); return (-1);
}
return cloudinit_fetch(sc);
} }
static int static int

View file

@ -56,6 +56,7 @@ static void agent_unconfigure(void);
static char *metadata_parse(char *, size_t, enum strtype); static char *metadata_parse(char *, size_t, enum strtype);
static int agent_timeout; static int agent_timeout;
static char *cloudnames[] = CLOUDNAMES;
int int
shell(const char *arg, ...) shell(const char *arg, ...)
@ -970,7 +971,8 @@ metadata(struct system_config *sc, const char *path, enum strtype type)
g = http_get(&sc->sc_addr, 1, g = http_get(&sc->sc_addr, 1,
sc->sc_endpoint, 80, path, NULL, 0, NULL); sc->sc_endpoint, 80, path, NULL, 0, NULL);
if (g != NULL) if (g != NULL)
log_debug("%s: HTTP %d %s", __func__, g->code, path); log_debug("%s: HTTP %d http://%s%s", __func__, g->code,
sc->sc_endpoint, path);
if (g != NULL && g->code == 200 && g->bodypartsz > 0) if (g != NULL && g->code == 200 && g->bodypartsz > 0)
str = metadata_parse(g->bodypart, g->bodypartsz, type); str = metadata_parse(g->bodypart, g->bodypartsz, type);
@ -1190,20 +1192,118 @@ pwgen(size_t len, char **hash)
return (password); return (password);
} }
static void
cloud_add(enum cloudname name, struct clouds *clouds)
{
struct cloud *cloud;
TAILQ_FOREACH(cloud, clouds, cloud_entry) {
if (cloud->cloud_name == name)
fatalx("cloud %s defined twice",
cloudnames[name]);
}
if ((cloud = calloc(1, sizeof(*cloud))) == NULL)
fatal("%s: calloc", __func__);
cloud->cloud_name = name;
switch (name) {
case AZURE:
cloud->fetch = azure;
break;
case CLOUDINIT:
cloud->fetch = cloudinit;
break;
case EC2:
cloud->fetch = ec2;
break;
case OPENNEBULA:
cloud->fetch = opennebula;
break;
case OPENSTACK:
cloud->fetch = openstack;
break;
}
TAILQ_INSERT_TAIL(clouds, cloud, cloud_entry);
}
static int
trycloud(struct system_config *sc, struct cloud *cloud)
{
int errfd = -1, ret = -1;
free(sc->sc_endpoint);
sc->sc_endpoint = NULL;
switch (sc->sc_state) {
case STATE_INIT:
if ((cloud = TAILQ_FIRST(sc->sc_clouds)) == NULL)
return (0);
sc->sc_stack = cloudnames[cloud->cloud_name];
log_debug("%s: %s", __func__, sc->sc_stack);
TAILQ_REMOVE(sc->sc_clouds, cloud, cloud_entry);
break;
case STATE_DHCP:
sc->sc_state = STATE_169;
if (dhcp_getendpoint(sc) == -1)
return trycloud(sc, cloud);
break;
case STATE_169:
sc->sc_state = STATE_DONE;
if ((sc->sc_endpoint = strdup(DEFAULT_ENDPOINT)) == NULL) {
log_warnx("failed to set defaults");
goto done;
}
break;
case STATE_DONE:
sc->sc_state = STATE_INIT;
ret = trycloud(sc, NULL);
goto done;
}
errfd = disable_output(sc, STDERR_FILENO);
ret = (*cloud->fetch)(sc);
enable_output(sc, STDERR_FILENO, errfd);
if (ret != 0)
return trycloud(sc, cloud);
done:
free(cloud);
return (0);
}
int int
main(int argc, char *const *argv) main(int argc, char *const *argv)
{ {
struct system_config *sc; struct system_config *sc;
int verbose = 0, dryrun = 0, unconfigure = 0; int verbose = 0, dryrun = 0, unconfigure = 0, sub;
int genpw = 0, ch, ret, timeout = CONNECT_TIMEOUT; int genpw = 0, ch, ret, timeout = CONNECT_TIMEOUT;
const char *error = NULL, *rootdisk = NULL; const char *error = NULL, *rootdisk = NULL;
char *args, *username = NULL; char *args, *username = NULL, *options, *value;
struct clouds clouds;
/* log to stderr */
log_init(1, LOG_DAEMON);
if ((args = get_args(argc, argv)) == NULL) if ((args = get_args(argc, argv)) == NULL)
fatalx("failed to save args"); fatalx("failed to save args");
while ((ch = getopt(argc, argv, "np:r:t:U:uv")) != -1) { TAILQ_INIT(&clouds);
while ((ch = getopt(argc, argv, "c:np:r:t:U:uv")) != -1) {
switch (ch) { switch (ch) {
case 'c':
options = optarg;
while (*options) {
if ((sub = getsubopt(&options,
cloudnames, &value)) == -1)
fatalx("invalid cloud stack");
else if (value != NULL)
fatalx("unexpected value");
cloud_add(sub, &clouds);
}
break;
case 'n': case 'n':
dryrun = 1; dryrun = 1;
break; break;
@ -1238,8 +1338,6 @@ main(int argc, char *const *argv)
argv += optind; argv += optind;
argc -= optind; argc -= optind;
/* log to stderr */
log_init(1, LOG_DAEMON);
log_setverbose(verbose); log_setverbose(verbose);
if (unconfigure) { if (unconfigure) {
@ -1262,24 +1360,30 @@ main(int argc, char *const *argv)
if (rootdisk != NULL && growdisk(sc) == -1) if (rootdisk != NULL && growdisk(sc) == -1)
fatalx("failed to grow %s", rootdisk); fatalx("failed to grow %s", rootdisk);
sc->sc_clouds = &clouds;
sc->sc_args = args; sc->sc_args = args;
if (username != NULL) { if (username != NULL) {
free(sc->sc_username); free(sc->sc_username);
sc->sc_username = username; sc->sc_username = username;
} }
/* if (TAILQ_EMPTY(&clouds)) {
* XXX Detect cloud with help from hostctl and sysctl /*
* XXX in addition to the interface name. * XXX Auto-detect cloud with help from hostctl and
*/ * XXX sysctl in addition to the interface name.
if (opennebula(sc) == 0) */
ret = 0; if (strcmp("hvn0", sc->sc_interface) == 0) {
else if (strcmp("hvn0", sc->sc_interface) == 0) cloud_add(AZURE, &clouds);
ret = azure(sc); } else if (strcmp("xnf0", sc->sc_interface) == 0) {
else if (strcmp("xnf0", sc->sc_interface) == 0) cloud_add(OPENNEBULA, &clouds);
ret = ec2(sc); cloud_add(EC2, &clouds);
else } else {
ret = openstack(sc); cloud_add(OPENNEBULA, &clouds);
cloud_add(OPENSTACK, &clouds);
cloud_add(CLOUDINIT, &clouds);
}
}
ret = trycloud(sc, NULL);
if (genpw) { if (genpw) {
if (sc->sc_password_hash != NULL) if (sc->sc_password_hash != NULL)

View file

@ -29,12 +29,30 @@
#define DEFAULT_ENDPOINT "169.254.169.254" #define DEFAULT_ENDPOINT "169.254.169.254"
#define CONNECT_TIMEOUT 10 /* in seconds */ #define CONNECT_TIMEOUT 10 /* in seconds */
enum cloudname {
AZURE,
CLOUDINIT,
EC2,
OPENNEBULA,
OPENSTACK
};
#define CLOUDNAMES { \
"azure", "cloudinit", "ec2", "opennebula", "openstack", NULL \
}
enum strtype { enum strtype {
WORD, WORD,
LINE, LINE,
TEXT TEXT
}; };
enum state {
STATE_INIT,
STATE_DHCP,
STATE_169,
STATE_DONE
};
struct ssh_pubkey { struct ssh_pubkey {
char *ssh_keyval; char *ssh_keyval;
char *ssh_keyfp; char *ssh_keyfp;
@ -66,6 +84,14 @@ struct net_addr {
}; };
TAILQ_HEAD(net_addrs, net_addr); TAILQ_HEAD(net_addrs, net_addr);
struct system_config;
struct cloud {
enum cloudname cloud_name;
int (*fetch)(struct system_config *);
TAILQ_ENTRY(cloud) cloud_entry;
};
TAILQ_HEAD(clouds, cloud);
struct system_config { struct system_config {
const char *sc_stack; const char *sc_stack;
char *sc_args; char *sc_args;
@ -77,7 +103,8 @@ struct system_config {
char *sc_pubkey; char *sc_pubkey;
char *sc_userdata; char *sc_userdata;
char *sc_endpoint; char *sc_endpoint;
int sc_dhcpendpoint; enum state sc_state;
struct clouds *sc_clouds;
char *sc_instance; char *sc_instance;
int sc_timeout; int sc_timeout;
@ -125,9 +152,6 @@ int azure(struct system_config *);
/* cloudinit.c */ /* cloudinit.c */
int ec2(struct system_config *); int ec2(struct system_config *);
int cloudinit(struct system_config *); int cloudinit(struct system_config *);
int tryendpoint(struct system_config *,
int (fetch)(struct system_config *),
int (next)(struct system_config *));
/* opennebula.c */ /* opennebula.c */
int opennebula(struct system_config *); int opennebula(struct system_config *);

View file

@ -40,12 +40,15 @@ opennebula(struct system_config *sc)
int ret = -1; int ret = -1;
unsigned short unit; unsigned short unit;
if (sc->sc_state == STATE_INIT) {
sc->sc_state = STATE_169;
return (-1);
}
/* Return silently without error */ /* Return silently without error */
if ((fp = fopen("/mnt/context.sh", "r")) == NULL) if ((fp = fopen("/mnt/context.sh", "r")) == NULL)
goto done; goto done;
sc->sc_stack = "opennebula";
while ((line = fparseln(fp, &len, &lineno, while ((line = fparseln(fp, &len, &lineno,
delim, FPARSELN_UNESCALL)) != NULL) { delim, FPARSELN_UNESCALL)) != NULL) {
/* key */ /* key */

View file

@ -31,11 +31,14 @@
static int openstack_fetch(struct system_config *); static int openstack_fetch(struct system_config *);
int int
openstack(struct system_config *sc) openstack(struct system_config *sc)
{ {
return tryendpoint(sc, openstack_fetch, cloudinit); if (sc->sc_state == STATE_INIT) {
sc->sc_state = STATE_DHCP;
return (-1);
}
return openstack_fetch(sc);
} }
static int static int
@ -53,7 +56,6 @@ openstack_fetch(struct system_config *sc)
if ((json = metadata(sc, if ((json = metadata(sc,
"/openstack/latest/meta_data.json", TEXT)) == NULL) "/openstack/latest/meta_data.json", TEXT)) == NULL)
goto fail; goto fail;
sc->sc_stack = "openstack";
if ((j = json_parse(json, strlen(json))) == NULL) if ((j = json_parse(json, strlen(json))) == NULL)
goto fail; goto fail;