Allow to specify the probed cloud stacks with -c cloud[,cloud...]
This commit is contained in:
parent
ba34eb76dd
commit
ca22cba8e4
7 changed files with 210 additions and 85 deletions
|
@ -63,16 +63,22 @@ azure(struct system_config *sc)
|
|||
{
|
||||
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 */
|
||||
free(sc->sc_username);
|
||||
if ((sc->sc_username = strdup("azure-user")) == NULL) {
|
||||
log_warnx("failed to set default user");
|
||||
goto fail;
|
||||
/* Apply defaults */
|
||||
sc->sc_ovfenv = "/var/db/azure-ovf-env.xml";
|
||||
sc->sc_priv = &az_config;
|
||||
sc->sc_state = STATE_DHCP;
|
||||
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) {
|
||||
log_warnx("failed to get ovf-env.xml");
|
||||
|
|
|
@ -23,6 +23,7 @@
|
|||
.Sh SYNOPSIS
|
||||
.Nm cloud-agent
|
||||
.Op Fl nuv
|
||||
.Op Fl c Ar cloud Ns Op , Ns Ar cloud Ns ...
|
||||
.Op Fl p Ar length
|
||||
.Op Fl r Ar rootdisk
|
||||
.Op Fl t Ar timeout
|
||||
|
@ -32,10 +33,31 @@
|
|||
The
|
||||
.Nm
|
||||
program manages the OpenBSD provisioning and VM interaction in cloud
|
||||
environments, including Microsoft Azure and Amazon AWS.
|
||||
environments.
|
||||
.Pp
|
||||
The options are as follows:
|
||||
.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
|
||||
Generate and set a random password for the default user.
|
||||
The password will be written in its plain form into the
|
||||
|
|
|
@ -31,65 +31,29 @@
|
|||
|
||||
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
|
||||
ec2(struct system_config *sc)
|
||||
{
|
||||
free(sc->sc_username);
|
||||
if ((sc->sc_username = strdup("ec2-user")) == NULL) {
|
||||
log_warnx("failed to set default user");
|
||||
if (sc->sc_state == STATE_INIT) {
|
||||
free(sc->sc_username);
|
||||
if ((sc->sc_username = strdup("ec2-user")) == NULL) {
|
||||
log_warnx("failed to set default user");
|
||||
return (-1);
|
||||
}
|
||||
sc->sc_state = STATE_169;
|
||||
return (-1);
|
||||
}
|
||||
|
||||
sc->sc_stack = "ec2";
|
||||
sc->sc_dhcpendpoint = 1;
|
||||
return tryendpoint(sc, cloudinit_fetch, NULL);
|
||||
return cloudinit_fetch(sc);
|
||||
}
|
||||
|
||||
int
|
||||
cloudinit(struct system_config *sc)
|
||||
{
|
||||
sc->sc_stack = "cloudinit";
|
||||
sc->sc_dhcpendpoint = 0;
|
||||
return tryendpoint(sc, cloudinit_fetch, NULL);
|
||||
if (sc->sc_state == STATE_INIT) {
|
||||
sc->sc_state = STATE_DHCP;
|
||||
return (-1);
|
||||
}
|
||||
return cloudinit_fetch(sc);
|
||||
}
|
||||
|
||||
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 int agent_timeout;
|
||||
static char *cloudnames[] = CLOUDNAMES;
|
||||
|
||||
int
|
||||
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,
|
||||
sc->sc_endpoint, 80, path, NULL, 0, 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)
|
||||
str = metadata_parse(g->bodypart, g->bodypartsz, type);
|
||||
|
@ -1190,20 +1192,118 @@ pwgen(size_t len, char **hash)
|
|||
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
|
||||
main(int argc, char *const *argv)
|
||||
{
|
||||
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;
|
||||
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)
|
||||
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) {
|
||||
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':
|
||||
dryrun = 1;
|
||||
break;
|
||||
|
@ -1238,8 +1338,6 @@ main(int argc, char *const *argv)
|
|||
argv += optind;
|
||||
argc -= optind;
|
||||
|
||||
/* log to stderr */
|
||||
log_init(1, LOG_DAEMON);
|
||||
log_setverbose(verbose);
|
||||
|
||||
if (unconfigure) {
|
||||
|
@ -1262,24 +1360,30 @@ main(int argc, char *const *argv)
|
|||
if (rootdisk != NULL && growdisk(sc) == -1)
|
||||
fatalx("failed to grow %s", rootdisk);
|
||||
|
||||
sc->sc_clouds = &clouds;
|
||||
sc->sc_args = args;
|
||||
if (username != NULL) {
|
||||
free(sc->sc_username);
|
||||
sc->sc_username = username;
|
||||
}
|
||||
|
||||
/*
|
||||
* XXX Detect cloud with help from hostctl and sysctl
|
||||
* XXX in addition to the interface name.
|
||||
*/
|
||||
if (opennebula(sc) == 0)
|
||||
ret = 0;
|
||||
else if (strcmp("hvn0", sc->sc_interface) == 0)
|
||||
ret = azure(sc);
|
||||
else if (strcmp("xnf0", sc->sc_interface) == 0)
|
||||
ret = ec2(sc);
|
||||
else
|
||||
ret = openstack(sc);
|
||||
if (TAILQ_EMPTY(&clouds)) {
|
||||
/*
|
||||
* XXX Auto-detect cloud with help from hostctl and
|
||||
* XXX sysctl in addition to the interface name.
|
||||
*/
|
||||
if (strcmp("hvn0", sc->sc_interface) == 0) {
|
||||
cloud_add(AZURE, &clouds);
|
||||
} else if (strcmp("xnf0", sc->sc_interface) == 0) {
|
||||
cloud_add(OPENNEBULA, &clouds);
|
||||
cloud_add(EC2, &clouds);
|
||||
} else {
|
||||
cloud_add(OPENNEBULA, &clouds);
|
||||
cloud_add(OPENSTACK, &clouds);
|
||||
cloud_add(CLOUDINIT, &clouds);
|
||||
}
|
||||
}
|
||||
ret = trycloud(sc, NULL);
|
||||
|
||||
if (genpw) {
|
||||
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 CONNECT_TIMEOUT 10 /* in seconds */
|
||||
|
||||
enum cloudname {
|
||||
AZURE,
|
||||
CLOUDINIT,
|
||||
EC2,
|
||||
OPENNEBULA,
|
||||
OPENSTACK
|
||||
};
|
||||
#define CLOUDNAMES { \
|
||||
"azure", "cloudinit", "ec2", "opennebula", "openstack", NULL \
|
||||
}
|
||||
|
||||
enum strtype {
|
||||
WORD,
|
||||
LINE,
|
||||
TEXT
|
||||
};
|
||||
|
||||
enum state {
|
||||
STATE_INIT,
|
||||
STATE_DHCP,
|
||||
STATE_169,
|
||||
STATE_DONE
|
||||
};
|
||||
|
||||
struct ssh_pubkey {
|
||||
char *ssh_keyval;
|
||||
char *ssh_keyfp;
|
||||
|
@ -66,6 +84,14 @@ struct 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 {
|
||||
const char *sc_stack;
|
||||
char *sc_args;
|
||||
|
@ -77,7 +103,8 @@ struct system_config {
|
|||
char *sc_pubkey;
|
||||
char *sc_userdata;
|
||||
char *sc_endpoint;
|
||||
int sc_dhcpendpoint;
|
||||
enum state sc_state;
|
||||
struct clouds *sc_clouds;
|
||||
char *sc_instance;
|
||||
int sc_timeout;
|
||||
|
||||
|
@ -125,9 +152,6 @@ int azure(struct system_config *);
|
|||
/* cloudinit.c */
|
||||
int ec2(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 */
|
||||
int opennebula(struct system_config *);
|
||||
|
|
|
@ -40,12 +40,15 @@ opennebula(struct system_config *sc)
|
|||
int ret = -1;
|
||||
unsigned short unit;
|
||||
|
||||
if (sc->sc_state == STATE_INIT) {
|
||||
sc->sc_state = STATE_169;
|
||||
return (-1);
|
||||
}
|
||||
|
||||
/* Return silently without error */
|
||||
if ((fp = fopen("/mnt/context.sh", "r")) == NULL)
|
||||
goto done;
|
||||
|
||||
sc->sc_stack = "opennebula";
|
||||
|
||||
while ((line = fparseln(fp, &len, &lineno,
|
||||
delim, FPARSELN_UNESCALL)) != NULL) {
|
||||
/* key */
|
||||
|
|
|
@ -31,11 +31,14 @@
|
|||
|
||||
static int openstack_fetch(struct system_config *);
|
||||
|
||||
|
||||
int
|
||||
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
|
||||
|
@ -53,7 +56,6 @@ openstack_fetch(struct system_config *sc)
|
|||
if ((json = metadata(sc,
|
||||
"/openstack/latest/meta_data.json", TEXT)) == NULL)
|
||||
goto fail;
|
||||
sc->sc_stack = "openstack";
|
||||
|
||||
if ((j = json_parse(json, strlen(json))) == NULL)
|
||||
goto fail;
|
||||
|
|
Loading…
Reference in a new issue