Merge pull request #7 from reyk/probe-order
Allow to specify the probed cloud stacks with -c cloud[,cloud...]
This commit is contained in:
commit
678b29acef
7 changed files with 210 additions and 85 deletions
|
@ -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");
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
140
agent/main.c
140
agent/main.c
|
@ -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)
|
||||||
|
|
32
agent/main.h
32
agent/main.h
|
@ -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 *);
|
||||||
|
|
|
@ -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 */
|
||||||
|
|
|
@ -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;
|
||||||
|
|
Loading…
Reference in a new issue