Add initial support for OpenNebula contextualization.
Thanks to datacenterlight.ch by ungleich glarus AG for providing access to their OpenNebula-based cloud.
This commit is contained in:
parent
ec87db177d
commit
d9899d488a
8 changed files with 449 additions and 25 deletions
|
@ -35,6 +35,9 @@ Installation is easy, `cloud-agent` detects the cloud type automatically.
|
||||||
|
|
||||||
* On OpenStack/VMware, create a file `/etc/hostname.vmx0`
|
* On OpenStack/VMware, create a file `/etc/hostname.vmx0`
|
||||||
|
|
||||||
|
* On OpenNebula, create a file `/etc/hostname.if`
|
||||||
|
where _if_ is the name of your primary interface.
|
||||||
|
|
||||||
* The content of the file is identical for all of them:
|
* The content of the file is identical for all of them:
|
||||||
|
|
||||||
dhcp
|
dhcp
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
PROG= cloud-agent
|
PROG= cloud-agent
|
||||||
SRCS= azure.c cloudinit.c http.c json.c jsmn.c log.c openstack.c main.c xml.c
|
SRCS= http.c json.c jsmn.c log.c main.c xml.c
|
||||||
|
SRCS+= azure.c cloudinit.c opennebula.c openstack.c
|
||||||
BINDIR= /usr/local/libexec
|
BINDIR= /usr/local/libexec
|
||||||
MANDIR= /usr/local/man/man
|
MANDIR= /usr/local/man/man
|
||||||
|
|
||||||
|
@ -15,7 +16,7 @@ CFLAGS+= -Wmissing-declarations
|
||||||
CFLAGS+= -Wshadow -Wpointer-arith
|
CFLAGS+= -Wshadow -Wpointer-arith
|
||||||
CFLAGS+= -Wsign-compare -Wcast-qual
|
CFLAGS+= -Wsign-compare -Wcast-qual
|
||||||
|
|
||||||
LDADD+= -lexpat -ltls -lssl -lcrypto
|
LDADD+= -lexpat -ltls -lssl -lcrypto -lutil
|
||||||
DPADD+= ${LIBEXPAT} ${LIBTLS} ${LIBSSL} ${LIBCRYPTO}
|
DPADD+= ${LIBEXPAT} ${LIBTLS} ${LIBSSL} ${LIBCRYPTO} ${LIBUTIL}
|
||||||
|
|
||||||
.include <bsd.prog.mk>
|
.include <bsd.prog.mk>
|
||||||
|
|
|
@ -62,13 +62,14 @@ azure(struct system_config *sc)
|
||||||
{
|
{
|
||||||
int ret = -1;
|
int ret = -1;
|
||||||
|
|
||||||
|
sc->sc_stack = "azure";
|
||||||
|
|
||||||
/* Apply defaults */
|
/* Apply defaults */
|
||||||
free(sc->sc_username);
|
free(sc->sc_username);
|
||||||
if ((sc->sc_username = strdup("azure-user")) == NULL) {
|
if ((sc->sc_username = strdup("azure-user")) == NULL) {
|
||||||
log_warnx("failed to set default user");
|
log_warnx("failed to set default user");
|
||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
sc->sc_cdrom = "/dev/cd0c";
|
|
||||||
sc->sc_ovfenv = "/var/db/azure-ovf-env.xml";
|
sc->sc_ovfenv = "/var/db/azure-ovf-env.xml";
|
||||||
sc->sc_priv = &az_config;
|
sc->sc_priv = &az_config;
|
||||||
|
|
||||||
|
@ -654,36 +655,22 @@ azure_getovfenv(struct system_config *sc)
|
||||||
struct xml xml;
|
struct xml xml;
|
||||||
struct xmlelem *xp, *xe, *xk, *xv;
|
struct xmlelem *xp, *xe, *xk, *xv;
|
||||||
char *sshfp, *sshval, *str;
|
char *sshfp, *sshval, *str;
|
||||||
int mount = 0, ret = -1, fd = -1;
|
int ret = -1, fd = -1;
|
||||||
FILE *fp;
|
FILE *fp;
|
||||||
|
|
||||||
/* Silently try to mount the cdrom */
|
|
||||||
fd = disable_output(sc, STDERR_FILENO);
|
|
||||||
ret = shell("mount", "-r", sc->sc_cdrom, "/mnt", NULL);
|
|
||||||
enable_output(sc, STDERR_FILENO, fd);
|
|
||||||
fd = -1;
|
|
||||||
|
|
||||||
if (ret == 0) {
|
|
||||||
log_debug("%s: mounted %s", __func__, sc->sc_cdrom);
|
|
||||||
mount = 1;
|
|
||||||
}
|
|
||||||
ret = -1;
|
|
||||||
|
|
||||||
if (xml_init(&xml) != 0) {
|
if (xml_init(&xml) != 0) {
|
||||||
log_debug("%s: xml", __func__);
|
log_debug("%s: xml", __func__);
|
||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Fallback to and older ovf-env.xml file */
|
/*
|
||||||
|
* Assume that the cdrom is already mounted.
|
||||||
|
* Fallback to and older ovf-env.xml file.
|
||||||
|
*/
|
||||||
if (xml_parse(&xml, "/mnt/ovf-env.xml") == -1 &&
|
if (xml_parse(&xml, "/mnt/ovf-env.xml") == -1 &&
|
||||||
xml_parse(&xml, sc->sc_ovfenv) == -1)
|
xml_parse(&xml, sc->sc_ovfenv) == -1)
|
||||||
goto done;
|
goto done;
|
||||||
|
|
||||||
/* unmount if we mounted the cdrom before */
|
|
||||||
if (mount && shell("umount", "/mnt", NULL) == 0) {
|
|
||||||
log_debug("%s: unmounted %s", __func__, sc->sc_cdrom);
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((xp = xml_findl(&xml.ox_root,
|
if ((xp = xml_findl(&xml.ox_root,
|
||||||
"Environment", "wa:ProvisioningSection",
|
"Environment", "wa:ProvisioningSection",
|
||||||
"LinuxProvisioningConfigurationSet", NULL)) == NULL) {
|
"LinuxProvisioningConfigurationSet", NULL)) == NULL) {
|
||||||
|
|
|
@ -41,6 +41,7 @@ ec2(struct system_config *sc)
|
||||||
return (-1);
|
return (-1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
sc->sc_stack = "ec2";
|
||||||
return (cloudinit_fetch(sc));
|
return (cloudinit_fetch(sc));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -53,6 +54,7 @@ cloudinit(struct system_config *sc)
|
||||||
return (-1);
|
return (-1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
sc->sc_stack = "cloudinit";
|
||||||
return (cloudinit_fetch(sc));
|
return (cloudinit_fetch(sc));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
188
agent/main.c
188
agent/main.c
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2017 Reyk Floeter <reyk@openbsd.org>
|
* Copyright (c) 2017, 2018 Reyk Floeter <reyk@openbsd.org>
|
||||||
*
|
*
|
||||||
* Permission to use, copy, modify, and distribute this software for any
|
* Permission to use, copy, modify, and distribute this software for any
|
||||||
* purpose with or without fee is hereby granted, provided that the above
|
* purpose with or without fee is hereby granted, provided that the above
|
||||||
|
@ -15,19 +15,24 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <sys/wait.h>
|
#include <sys/wait.h>
|
||||||
|
#include <sys/types.h>
|
||||||
#include <sys/socket.h>
|
#include <sys/socket.h>
|
||||||
#include <sys/stat.h>
|
#include <sys/stat.h>
|
||||||
|
|
||||||
|
#include <net/if.h>
|
||||||
#include <netinet/in.h>
|
#include <netinet/in.h>
|
||||||
|
#include <netinet/if_ether.h>
|
||||||
|
|
||||||
#include <limits.h>
|
#include <limits.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <syslog.h>
|
#include <syslog.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
#include <stdint.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <signal.h>
|
#include <signal.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#include <resolv.h>
|
#include <resolv.h>
|
||||||
|
#include <netdb.h>
|
||||||
#include <ctype.h>
|
#include <ctype.h>
|
||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
|
@ -40,6 +45,7 @@
|
||||||
__dead void usage(void);
|
__dead void usage(void);
|
||||||
static struct system_config *agent_init(const char *, int, int);
|
static struct system_config *agent_init(const char *, int, int);
|
||||||
static int agent_configure(struct system_config *);
|
static int agent_configure(struct system_config *);
|
||||||
|
static int agent_network(struct system_config *);
|
||||||
static void agent_free(struct system_config *);
|
static void agent_free(struct system_config *);
|
||||||
static int agent_pf(struct system_config *, int);
|
static int agent_pf(struct system_config *, int);
|
||||||
static int agent_userdata(const unsigned char *, size_t);
|
static int agent_userdata(const unsigned char *, size_t);
|
||||||
|
@ -303,14 +309,17 @@ static struct system_config *
|
||||||
agent_init(const char *ifname, int dryrun, int timeout)
|
agent_init(const char *ifname, int dryrun, int timeout)
|
||||||
{
|
{
|
||||||
struct system_config *sc;
|
struct system_config *sc;
|
||||||
|
int fd, ret;
|
||||||
|
|
||||||
if ((sc = calloc(1, sizeof(*sc))) == NULL)
|
if ((sc = calloc(1, sizeof(*sc))) == NULL)
|
||||||
return (NULL);
|
return (NULL);
|
||||||
|
|
||||||
sc->sc_interface = ifname;
|
sc->sc_interface = ifname;
|
||||||
|
sc->sc_cdrom = "/dev/cd0c";
|
||||||
sc->sc_dryrun = dryrun ? 1 : 0;
|
sc->sc_dryrun = dryrun ? 1 : 0;
|
||||||
sc->sc_timeout = agent_timeout = timeout < 1 ? -1 : timeout * 1000;
|
sc->sc_timeout = agent_timeout = timeout < 1 ? -1 : timeout * 1000;
|
||||||
TAILQ_INIT(&sc->sc_pubkeys);
|
TAILQ_INIT(&sc->sc_pubkeys);
|
||||||
|
TAILQ_INIT(&sc->sc_netaddrs);
|
||||||
|
|
||||||
if ((sc->sc_nullfd = open("/dev/null", O_RDWR)) == -1) {
|
if ((sc->sc_nullfd = open("/dev/null", O_RDWR)) == -1) {
|
||||||
free(sc);
|
free(sc);
|
||||||
|
@ -322,6 +331,15 @@ agent_init(const char *ifname, int dryrun, int timeout)
|
||||||
return (NULL);
|
return (NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Silently try to mount the cdrom */
|
||||||
|
fd = disable_output(sc, STDERR_FILENO);
|
||||||
|
ret = shell("mount", "-r", sc->sc_cdrom, "/mnt", NULL);
|
||||||
|
enable_output(sc, STDERR_FILENO, fd);
|
||||||
|
if (ret == 0) {
|
||||||
|
log_debug("%s: mounted %s", __func__, sc->sc_cdrom);
|
||||||
|
sc->sc_mount = 1;
|
||||||
|
}
|
||||||
|
|
||||||
if (sc->sc_dryrun)
|
if (sc->sc_dryrun)
|
||||||
return (sc);
|
return (sc);
|
||||||
|
|
||||||
|
@ -337,6 +355,12 @@ static void
|
||||||
agent_free(struct system_config *sc)
|
agent_free(struct system_config *sc)
|
||||||
{
|
{
|
||||||
struct ssh_pubkey *ssh;
|
struct ssh_pubkey *ssh;
|
||||||
|
struct net_addr *net;
|
||||||
|
|
||||||
|
/* unmount if we mounted the cdrom before */
|
||||||
|
if (sc->sc_mount && shell("umount", "/mnt", NULL) == 0) {
|
||||||
|
log_debug("%s: unmounted %s", __func__, sc->sc_cdrom);
|
||||||
|
}
|
||||||
|
|
||||||
free(sc->sc_hostname);
|
free(sc->sc_hostname);
|
||||||
free(sc->sc_username);
|
free(sc->sc_username);
|
||||||
|
@ -352,6 +376,12 @@ agent_free(struct system_config *sc)
|
||||||
TAILQ_REMOVE(&sc->sc_pubkeys, ssh, ssh_entry);
|
TAILQ_REMOVE(&sc->sc_pubkeys, ssh, ssh_entry);
|
||||||
free(ssh);
|
free(ssh);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
while ((net = TAILQ_FIRST(&sc->sc_netaddrs))) {
|
||||||
|
TAILQ_REMOVE(&sc->sc_netaddrs, net, net_entry);
|
||||||
|
free(net->net_value);
|
||||||
|
free(net);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int
|
int
|
||||||
|
@ -405,6 +435,103 @@ agent_setpubkey(struct system_config *sc, const char *sshval, const char *sshfp)
|
||||||
return (ret);
|
return (ret);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct net_addr *
|
||||||
|
agent_getnetaddr(struct system_config *sc, struct net_addr *net)
|
||||||
|
{
|
||||||
|
struct net_addr *na;
|
||||||
|
|
||||||
|
TAILQ_FOREACH(na, &sc->sc_netaddrs, net_entry) {
|
||||||
|
if (na->net_type != net->net_type)
|
||||||
|
continue;
|
||||||
|
if (na->net_ifunit != net->net_ifunit)
|
||||||
|
continue;
|
||||||
|
if (net->net_addr.ss_family != AF_UNSPEC) {
|
||||||
|
if (na->net_addr.ss_family !=
|
||||||
|
net->net_addr.ss_family)
|
||||||
|
continue;
|
||||||
|
if (memcmp(&na->net_addr, &net->net_addr,
|
||||||
|
na->net_addr.ss_len) != 0)
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (na);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
agent_addnetaddr(struct system_config *sc, unsigned int unit,
|
||||||
|
const char *value, int af, enum net_type type)
|
||||||
|
{
|
||||||
|
const char *errstr;
|
||||||
|
struct addrinfo hints, *res;
|
||||||
|
struct net_addr *net, *na;
|
||||||
|
|
||||||
|
if ((net = calloc(1, sizeof(*net))) == NULL) {
|
||||||
|
log_debug("%s: calloc", __func__);
|
||||||
|
return (-1);
|
||||||
|
}
|
||||||
|
net->net_ifunit = unit;
|
||||||
|
net->net_type = type;
|
||||||
|
|
||||||
|
switch (type) {
|
||||||
|
case NET_MAC:
|
||||||
|
if (ether_aton(value) == NULL) {
|
||||||
|
log_debug("%s: if%u mac %s", __func__, unit, value);
|
||||||
|
free(net);
|
||||||
|
return (-1);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case NET_MTU:
|
||||||
|
case NET_PREFIX:
|
||||||
|
net->net_num = strtonum(value, 0, UINT32_MAX, &errstr);
|
||||||
|
if (errstr != NULL) {
|
||||||
|
log_debug("%s: if%u %s", __func__, unit, value);
|
||||||
|
free(net);
|
||||||
|
return (-1);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
memset(&hints, 0, sizeof(hints));
|
||||||
|
hints.ai_family = af;
|
||||||
|
hints.ai_socktype = SOCK_DGRAM;
|
||||||
|
hints.ai_flags = AI_NUMERICHOST;
|
||||||
|
if (getaddrinfo(value, "0", &hints, &res) != 0) {
|
||||||
|
log_debug("%s: invalid address %s",
|
||||||
|
__func__, value);
|
||||||
|
free(net);
|
||||||
|
return (-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (res->ai_addrlen > sizeof(net->net_addr)) {
|
||||||
|
log_debug("%s: address too long",
|
||||||
|
__func__);
|
||||||
|
free(net);
|
||||||
|
freeaddrinfo(res);
|
||||||
|
return (-1);
|
||||||
|
}
|
||||||
|
memcpy(&net->net_addr, res->ai_addr, res->ai_addrlen);
|
||||||
|
net->net_addr.ss_len = res->ai_addrlen;
|
||||||
|
net->net_addr.ss_family = res->ai_family;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Address already exists, ignore new entry */
|
||||||
|
if ((na = agent_getnetaddr(sc, net)) != NULL) {
|
||||||
|
free(net);
|
||||||
|
return (0);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((net->net_value = strdup(value)) == NULL) {
|
||||||
|
free(net);
|
||||||
|
return (-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
TAILQ_INSERT_TAIL(&sc->sc_netaddrs, net, net_entry);
|
||||||
|
|
||||||
|
return (0);
|
||||||
|
}
|
||||||
|
|
||||||
static int
|
static int
|
||||||
fileout(const char *str, const char *mode, const char *fmt, ...)
|
fileout(const char *str, const char *mode, const char *fmt, ...)
|
||||||
{
|
{
|
||||||
|
@ -575,6 +702,9 @@ agent_configure(struct system_config *sc)
|
||||||
log_warnx("user-data failed");
|
log_warnx("user-data failed");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (agent_network(sc) != 0)
|
||||||
|
log_warnx("network configuration failed");
|
||||||
|
|
||||||
log_debug("%s: %s", __func__, "/etc/rc.firsttime");
|
log_debug("%s: %s", __func__, "/etc/rc.firsttime");
|
||||||
if (fileout("logger -s -t cloud-agent <<EOF\n"
|
if (fileout("logger -s -t cloud-agent <<EOF\n"
|
||||||
"#############################################################\n"
|
"#############################################################\n"
|
||||||
|
@ -658,6 +788,55 @@ agent_userdata(const unsigned char *userdata, size_t len)
|
||||||
return (ret);
|
return (ret);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
agent_network(struct system_config *sc)
|
||||||
|
{
|
||||||
|
struct net_addr *net;
|
||||||
|
char ift[16], ifname[16], line[1024], path[PATH_MAX];
|
||||||
|
const char *family;
|
||||||
|
|
||||||
|
if (!sc->sc_network)
|
||||||
|
return (0);
|
||||||
|
|
||||||
|
if (strlcpy(ift, sc->sc_interface, sizeof(ift)) >= sizeof(ift))
|
||||||
|
return (-1);
|
||||||
|
ift[strcspn(ift, "0123456789")] = '\0';
|
||||||
|
|
||||||
|
TAILQ_FOREACH(net, &sc->sc_netaddrs, net_entry) {
|
||||||
|
snprintf(ifname, sizeof(ifname), "%s%u", ift, net->net_ifunit);
|
||||||
|
switch (net->net_type) {
|
||||||
|
case NET_IP:
|
||||||
|
family = net->net_addr.ss_family == AF_INET ?
|
||||||
|
"inet" : "inet6";
|
||||||
|
/* XXX prefix */
|
||||||
|
|
||||||
|
/* hostname.if startup configuration */
|
||||||
|
snprintf(line, sizeof(line), "%s alias %s",
|
||||||
|
family, net->net_value);
|
||||||
|
snprintf(path, sizeof(path),
|
||||||
|
"/etc/hostname.%s", ifname);
|
||||||
|
fileout(line, "a", path);
|
||||||
|
|
||||||
|
/* runtime configuration */
|
||||||
|
(void)shell("ifconfig", ifname, family,
|
||||||
|
"alias", net->net_value, NULL);
|
||||||
|
break;
|
||||||
|
case NET_GATEWAY:
|
||||||
|
fileout(net->net_value, "a", "/etc/mygate");
|
||||||
|
break;
|
||||||
|
case NET_DNS:
|
||||||
|
snprintf(line, sizeof(line), "nameserver %s",
|
||||||
|
net->net_value);
|
||||||
|
fileout(line, "a", "/etc/resolv.conf");
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return (0);
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
agent_unconfigure(void)
|
agent_unconfigure(void)
|
||||||
{
|
{
|
||||||
|
@ -924,13 +1103,18 @@ main(int argc, char *const *argv)
|
||||||
* XXX Detect cloud with help from hostctl and sysctl
|
* XXX Detect cloud with help from hostctl and sysctl
|
||||||
* XXX in addition to the interface name.
|
* XXX in addition to the interface name.
|
||||||
*/
|
*/
|
||||||
if (strcmp("hvn0", sc->sc_interface) == 0)
|
if (opennebula(sc) == 0)
|
||||||
|
ret = 0;
|
||||||
|
else if (strcmp("hvn0", sc->sc_interface) == 0)
|
||||||
ret = azure(sc);
|
ret = azure(sc);
|
||||||
else if (strcmp("xnf0", sc->sc_interface) == 0)
|
else if (strcmp("xnf0", sc->sc_interface) == 0)
|
||||||
ret = ec2(sc);
|
ret = ec2(sc);
|
||||||
else
|
else
|
||||||
ret = openstack(sc);
|
ret = openstack(sc);
|
||||||
|
|
||||||
|
if (sc->sc_stack != NULL)
|
||||||
|
log_debug("%s: %s", __func__, sc->sc_stack);
|
||||||
|
|
||||||
if (sc->sc_dryrun) {
|
if (sc->sc_dryrun) {
|
||||||
agent_free(sc);
|
agent_free(sc);
|
||||||
return (0);
|
return (0);
|
||||||
|
|
36
agent/main.h
36
agent/main.h
|
@ -43,7 +43,31 @@ struct ssh_pubkey {
|
||||||
};
|
};
|
||||||
TAILQ_HEAD(ssh_pubkeys, ssh_pubkey);
|
TAILQ_HEAD(ssh_pubkeys, ssh_pubkey);
|
||||||
|
|
||||||
|
enum net_type {
|
||||||
|
NET_IP,
|
||||||
|
NET_MASK,
|
||||||
|
NET_PREFIX,
|
||||||
|
NET_MAC,
|
||||||
|
NET_MTU,
|
||||||
|
NET_GATEWAY,
|
||||||
|
NET_DNS,
|
||||||
|
NET_MAX
|
||||||
|
};
|
||||||
|
|
||||||
|
struct net_addr {
|
||||||
|
enum net_type net_type;
|
||||||
|
unsigned int net_ifunit;
|
||||||
|
char *net_value;
|
||||||
|
struct sockaddr_storage net_addr;
|
||||||
|
unsigned int net_num;
|
||||||
|
|
||||||
|
TAILQ_ENTRY(net_addr) net_entry;
|
||||||
|
};
|
||||||
|
TAILQ_HEAD(net_addrs, net_addr);
|
||||||
|
|
||||||
struct system_config {
|
struct system_config {
|
||||||
|
const char *sc_stack;
|
||||||
|
|
||||||
char *sc_hostname;
|
char *sc_hostname;
|
||||||
char *sc_username;
|
char *sc_username;
|
||||||
char *sc_password;
|
char *sc_password;
|
||||||
|
@ -56,10 +80,15 @@ struct system_config {
|
||||||
const char *sc_ovfenv;
|
const char *sc_ovfenv;
|
||||||
const char *sc_interface;
|
const char *sc_interface;
|
||||||
const char *sc_cdrom;
|
const char *sc_cdrom;
|
||||||
|
int sc_mount;
|
||||||
|
|
||||||
struct source sc_addr;
|
struct source sc_addr;
|
||||||
struct ssh_pubkeys sc_pubkeys;
|
struct ssh_pubkeys sc_pubkeys;
|
||||||
|
|
||||||
|
int sc_network;
|
||||||
|
struct net_addrs sc_netaddrs;
|
||||||
|
unsigned int sc_netmtu;
|
||||||
|
|
||||||
int sc_nullfd;
|
int sc_nullfd;
|
||||||
int sc_dryrun;
|
int sc_dryrun;
|
||||||
void *sc_priv;
|
void *sc_priv;
|
||||||
|
@ -92,6 +121,9 @@ int azure(struct system_config *);
|
||||||
int ec2(struct system_config *);
|
int ec2(struct system_config *);
|
||||||
int cloudinit(struct system_config *);
|
int cloudinit(struct system_config *);
|
||||||
|
|
||||||
|
/* opennebula.c */
|
||||||
|
int opennebula(struct system_config *);
|
||||||
|
|
||||||
/* openstack.c */
|
/* openstack.c */
|
||||||
int openstack(struct system_config *);
|
int openstack(struct system_config *);
|
||||||
|
|
||||||
|
@ -105,6 +137,10 @@ char *get_line(const unsigned char *, size_t);
|
||||||
char *get_word(const unsigned char *, size_t);
|
char *get_word(const unsigned char *, size_t);
|
||||||
int agent_addpubkey(struct system_config *, const char *, const char *);
|
int agent_addpubkey(struct system_config *, const char *, const char *);
|
||||||
int agent_setpubkey(struct system_config *, const char *, const char *);
|
int agent_setpubkey(struct system_config *, const char *, const char *);
|
||||||
|
struct net_addr *
|
||||||
|
agent_getnetaddr(struct system_config *, struct net_addr *);
|
||||||
|
int agent_addnetaddr(struct system_config *, unsigned int,
|
||||||
|
const char *, int, enum net_type);
|
||||||
char *metadata(struct system_config *, const char *, enum strtype);
|
char *metadata(struct system_config *, const char *, enum strtype);
|
||||||
char *metadata_file(struct system_config *, const char *, enum strtype);
|
char *metadata_file(struct system_config *, const char *, enum strtype);
|
||||||
int connect_wait(int, const struct sockaddr *, socklen_t);
|
int connect_wait(int, const struct sockaddr *, socklen_t);
|
||||||
|
|
209
agent/opennebula.c
Normal file
209
agent/opennebula.c
Normal file
|
@ -0,0 +1,209 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2018 Reyk Floeter <reyk@openbsd.org>
|
||||||
|
*
|
||||||
|
* Permission to use, copy, modify, and distribute this software for any
|
||||||
|
* purpose with or without fee is hereby granted, provided that the above
|
||||||
|
* copyright notice and this permission notice appear in all copies.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||||
|
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||||
|
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||||
|
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||||
|
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||||
|
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||||
|
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <fnmatch.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <ctype.h>
|
||||||
|
#include <sha2.h>
|
||||||
|
#include <err.h>
|
||||||
|
#include <util.h>
|
||||||
|
|
||||||
|
#include "main.h"
|
||||||
|
|
||||||
|
int
|
||||||
|
opennebula(struct system_config *sc)
|
||||||
|
{
|
||||||
|
FILE *fp;
|
||||||
|
const char *delim = "\\\\\0", *errstr;
|
||||||
|
char *line = NULL, *k, *v, *p, q;
|
||||||
|
char *hname = NULL;
|
||||||
|
size_t len, lineno = 0, i;
|
||||||
|
int ret = -1;
|
||||||
|
unsigned int unit;
|
||||||
|
|
||||||
|
/* 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 */
|
||||||
|
k = line;
|
||||||
|
|
||||||
|
/* a context always starts with this header */
|
||||||
|
if (lineno == 1) {
|
||||||
|
ret = strcmp(line,
|
||||||
|
"# Context variables generated by OpenNebula");
|
||||||
|
if (ret != 0) {
|
||||||
|
log_debug("%s: unsupported context", __func__);
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
free(line);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
line[strcspn(line, "#")] = '\0';
|
||||||
|
|
||||||
|
/* value */
|
||||||
|
if ((v = strchr(line, '=')) == NULL || *(v + 1) == '\0') {
|
||||||
|
free(line);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
*v++ = '\0';
|
||||||
|
|
||||||
|
/* value is quoted */
|
||||||
|
q = *v;
|
||||||
|
if (strspn(v, "\"'") == 0 || (p = strrchr(v, q)) == v) {
|
||||||
|
free(line);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
*v++ = '\0';
|
||||||
|
*p = '\0';
|
||||||
|
|
||||||
|
/* continue if value is empty */
|
||||||
|
if (*v == '\0') {
|
||||||
|
free(line);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
log_debug("%s: %s = %s", __func__, k, v);
|
||||||
|
|
||||||
|
if (strcasecmp("NETWORK", k) == 0) {
|
||||||
|
if (strcasecmp("YES", v) == 0)
|
||||||
|
sc->sc_network = 1;
|
||||||
|
else if (strcasecmp("YES", v) == 0)
|
||||||
|
sc->sc_network = 0;
|
||||||
|
} else if (fnmatch("ETH*_*", k, 0) != FNM_NOMATCH) {
|
||||||
|
/* Extract interface unit */
|
||||||
|
if ((p = strdup(k + 3)) == NULL) {
|
||||||
|
log_debug("%s: %s", __func__, k);
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
p[strcspn(p, "_")] = '\0';
|
||||||
|
unit = strtonum(p, 0, UINT32_MAX, &errstr);
|
||||||
|
free(p);
|
||||||
|
if (errstr != NULL) {
|
||||||
|
log_debug("%s: %s", __func__, k);
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Get subkey */
|
||||||
|
k += strcspn(k, "_") + 1;
|
||||||
|
|
||||||
|
if (strcasecmp("DNS", k) == 0) {
|
||||||
|
/* We don't support per-interface DNS */
|
||||||
|
for (p = v; *p != '\0'; v = p) {
|
||||||
|
p = v + strcspn(v, " \t");
|
||||||
|
*p++ = '\0';
|
||||||
|
if ((ret = agent_addnetaddr(sc, 0,
|
||||||
|
v, AF_UNSPEC, NET_DNS)) != 0)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} else if (strcasecmp("IP", k) == 0) {
|
||||||
|
ret = agent_addnetaddr(sc, unit,
|
||||||
|
v, AF_INET, NET_IP);
|
||||||
|
} else if (strcasecmp("MASK", k) == 0) {
|
||||||
|
ret = agent_addnetaddr(sc, unit,
|
||||||
|
v, AF_INET, NET_MASK);
|
||||||
|
} else if (strcasecmp("GATEWAY", k) == 0) {
|
||||||
|
ret = agent_addnetaddr(sc, unit,
|
||||||
|
v, AF_INET, NET_GATEWAY);
|
||||||
|
} else if (strcasecmp("IP6", k) == 0) {
|
||||||
|
ret = agent_addnetaddr(sc, unit,
|
||||||
|
v, AF_INET6, NET_IP);
|
||||||
|
} else if (strcasecmp("GATEWAY6", k) == 0) {
|
||||||
|
ret = agent_addnetaddr(sc, unit,
|
||||||
|
v, AF_INET6, NET_GATEWAY);
|
||||||
|
} else if (strcasecmp("PREFIX_LENGTH", k) == 0) {
|
||||||
|
ret = agent_addnetaddr(sc, unit,
|
||||||
|
v, AF_INET6, NET_PREFIX);
|
||||||
|
} else if (strcasecmp("MAC", k) == 0) {
|
||||||
|
if (unit == 0 && hname == NULL) {
|
||||||
|
/* Fake a hostname using the mac */
|
||||||
|
if ((hname = p = calloc(1,
|
||||||
|
strlen(v) + 3)) == NULL) {
|
||||||
|
log_debug("%s: calloc",
|
||||||
|
__func__);
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
*p++ = 'v';
|
||||||
|
*p++ = 'm';
|
||||||
|
for (i = 0; i < strlen(v); i++) {
|
||||||
|
if (!isalnum(v[i]))
|
||||||
|
continue;
|
||||||
|
*p++ = v[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = agent_addnetaddr(sc, unit,
|
||||||
|
v, AF_UNSPEC, NET_MAC);
|
||||||
|
} else if (strcasecmp("MTU", k) == 0) {
|
||||||
|
ret = agent_addnetaddr(sc, unit,
|
||||||
|
v, AF_UNSPEC, NET_MTU);
|
||||||
|
} else
|
||||||
|
ret = 0;
|
||||||
|
if (ret != 0) {
|
||||||
|
log_debug("%s: failed to parse %s",
|
||||||
|
__func__, k);
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
} else if (strcasecmp("SSH_PUBLIC_KEY", k) == 0) {
|
||||||
|
if (agent_addpubkey(sc, v, NULL) != 0)
|
||||||
|
log_warnx("failed to ssh pubkey");
|
||||||
|
}
|
||||||
|
|
||||||
|
free(line);
|
||||||
|
}
|
||||||
|
|
||||||
|
fclose(fp);
|
||||||
|
fp = NULL;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* OpenNebula doesn't provide an instance id so we
|
||||||
|
* calculate one using the hash of the context file.
|
||||||
|
* This might break if the context is not consistent.
|
||||||
|
*/
|
||||||
|
if ((sc->sc_instance =
|
||||||
|
calloc(1, SHA256_DIGEST_STRING_LENGTH)) == NULL ||
|
||||||
|
SHA256File("/mnt/context.sh", sc->sc_instance) == NULL) {
|
||||||
|
log_debug("%s: failed to calculate instance hash",
|
||||||
|
__func__);
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
log_debug("%s: context instance %s", __func__, sc->sc_instance);
|
||||||
|
|
||||||
|
/* Even the hostname is optional */
|
||||||
|
if (hname != NULL) {
|
||||||
|
free(sc->sc_hostname);
|
||||||
|
sc->sc_hostname = hname;
|
||||||
|
log_debug("%s: hostname %s", __func__, hname);
|
||||||
|
}
|
||||||
|
|
||||||
|
line = NULL;
|
||||||
|
ret = 0;
|
||||||
|
|
||||||
|
done:
|
||||||
|
if (fp != NULL)
|
||||||
|
fclose(fp);
|
||||||
|
free(line);
|
||||||
|
return (ret);
|
||||||
|
}
|
|
@ -44,6 +44,7 @@ openstack(struct system_config *sc)
|
||||||
free(sc->sc_endpoint);
|
free(sc->sc_endpoint);
|
||||||
return (cloudinit(sc));
|
return (cloudinit(sc));
|
||||||
}
|
}
|
||||||
|
|
||||||
return (0);
|
return (0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -62,6 +63,7 @@ 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