mirror of
https://github.com/kernelkit/infix.git
synced 2026-08-06 15:43:02 +02:00
confd: initial support for Docker containers using podman
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
This commit is contained in:
@@ -0,0 +1 @@
|
||||
local1.* -/var/log/container
|
||||
Executable
+220
@@ -0,0 +1,220 @@
|
||||
#!/bin/sh
|
||||
|
||||
all=""
|
||||
|
||||
log()
|
||||
{
|
||||
logger -I $PPID -t container -p local1.notice -- "$*"
|
||||
}
|
||||
|
||||
verify()
|
||||
{
|
||||
name=$1
|
||||
|
||||
for nm in $(podman ps -a --format "{{.Names}}"); do
|
||||
if [ "$name" = "$nm" ]; then
|
||||
return;
|
||||
fi
|
||||
done
|
||||
|
||||
>&2 echo "$name: no such container."
|
||||
exit 1
|
||||
}
|
||||
|
||||
# shellcheck disable=SC2086
|
||||
create()
|
||||
{
|
||||
name=$1
|
||||
image=$2
|
||||
shift 2
|
||||
|
||||
if [ -z "$name" ] || [ -z "$image" ]; then
|
||||
echo "Usage:"
|
||||
echo " container create NAME IMAGE"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
args="$args --restart=no --systemd=false --tz=local --privileged --replace --quiet"
|
||||
# args="$args --log-opt tag=$name"
|
||||
pidfn=/run/pod:${name}.pid
|
||||
|
||||
for net in "$@"; do
|
||||
network="$network --net $net"
|
||||
done
|
||||
log "---------------------------------------"
|
||||
log "Got name: $name image: $image"
|
||||
log "Got networks: $network"
|
||||
|
||||
if [ -n "$network" ]; then
|
||||
for srv in $dns; do
|
||||
args="$args --dns=$srv"
|
||||
done
|
||||
|
||||
for domain in $search; do
|
||||
args="$args --dns-search=$domain"
|
||||
done
|
||||
else
|
||||
network="--net none"
|
||||
fi
|
||||
|
||||
# --syslog --log-level info
|
||||
log "Calling \"podman create --name $name --conmon-pidfile=$pidfn $network $args $image\""
|
||||
if podman create --name "$name" --conmon-pidfile="$pidfn" $network $args "$image"; then
|
||||
log "Successfully created container $name from $image"
|
||||
initctl -nbq cond set pod:system
|
||||
exit 0
|
||||
fi
|
||||
log "Error: failed creating container $name, please check the configuration."
|
||||
exit 1
|
||||
}
|
||||
|
||||
delete()
|
||||
{
|
||||
name=$1
|
||||
image=$2
|
||||
|
||||
if [ -z "$name" ]; then
|
||||
echo "Usage:"
|
||||
echo " container delete NAME"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
podman kill -s KILL "$name" >/dev/null 2>&1
|
||||
podman rm -vif "$name" 2>&1
|
||||
log "Container $name has been removed."
|
||||
}
|
||||
|
||||
usage()
|
||||
{
|
||||
cat <<EOF
|
||||
usage:
|
||||
container [opt] cmd [arg]
|
||||
|
||||
options:
|
||||
-a, --all Show all, of something
|
||||
--dns NAMESERVER Set nameserver(s) when creating a container
|
||||
--dns-search LIST Set host lookup search list when creating container
|
||||
-f, --force Force operation, e.g. remove
|
||||
-h, --help Show this help text
|
||||
-s, --simple Show output in simplified format
|
||||
|
||||
commands:
|
||||
create NAME IMAGE NET Create container NAME using IMAGE with networks NET
|
||||
delete NAME Kill and remove container NAME
|
||||
help Show this help text
|
||||
list [image] List names (only) of containers or images
|
||||
remove IMAGE Remove an (unused) container image
|
||||
run NAME CMD Execute a command (interactively) in container
|
||||
show [image] Show containers or container images
|
||||
start NAME Start a container
|
||||
stop NAME Stop a container
|
||||
EOF
|
||||
}
|
||||
|
||||
while [ "$1" != "" ]; do
|
||||
case $1 in
|
||||
-a | --all)
|
||||
all="-a"
|
||||
;;
|
||||
--dns)
|
||||
shift
|
||||
dns="$dns $1"
|
||||
;;
|
||||
--dns-search)
|
||||
shift
|
||||
search="$search $1"
|
||||
;;
|
||||
-f | --force)
|
||||
force="-f"
|
||||
;;
|
||||
-h | --help)
|
||||
usage
|
||||
exit 0
|
||||
;;
|
||||
-s | --simple)
|
||||
simple=true
|
||||
;;
|
||||
*)
|
||||
break
|
||||
;;
|
||||
esac
|
||||
shift
|
||||
done
|
||||
|
||||
cmd=$1
|
||||
if [ -n "$cmd" ]; then
|
||||
shift
|
||||
fi
|
||||
|
||||
case $cmd in
|
||||
attach)
|
||||
podman exec -it "$1" "$2"
|
||||
;;
|
||||
create)
|
||||
log "Got create args: $*"
|
||||
create "$@"
|
||||
;;
|
||||
delete)
|
||||
delete "$@"
|
||||
;;
|
||||
help)
|
||||
usage
|
||||
;;
|
||||
ls | list)
|
||||
cmd=$1
|
||||
shift
|
||||
case $cmd in
|
||||
image*)
|
||||
podman images $all --format "{{.Repository}}:{{.Tag}}"
|
||||
;;
|
||||
*)
|
||||
podman ps $all --format "{{.Names}}"
|
||||
;;
|
||||
esac
|
||||
;;
|
||||
pull)
|
||||
podman pull "$@"
|
||||
;;
|
||||
remove)
|
||||
podman rmi $all $force -i "$1"
|
||||
;;
|
||||
run)
|
||||
echo "Starting container $1 :: use Ctrl-p Ctrl-q to exit"
|
||||
podman run -it --rm "$@"
|
||||
;;
|
||||
show)
|
||||
cmd=$1
|
||||
shift
|
||||
case $cmd in
|
||||
image*)
|
||||
if [ -n "$simple" ]; then
|
||||
podman images $all --format "{{.Names}} {{.Size}}" \
|
||||
| sed 's/\[\(.*\)\] /\1 /g' \
|
||||
| awk '{ printf "%-60s %s %s\n", $1, $2, $3}'
|
||||
else
|
||||
podman images $all
|
||||
fi
|
||||
;;
|
||||
*)
|
||||
if [ -n "$simple" ]; then
|
||||
podman ps $all --format "{{.ID}} {{.Names}} {{.Image}}" \
|
||||
| awk '{ printf "%s %-30s %s\n", $1, $2, $3}'
|
||||
else
|
||||
podman ps $all
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
;;
|
||||
start)
|
||||
verify "$1" || exit 1
|
||||
initctl -bq start "pod:$1"
|
||||
;;
|
||||
stop)
|
||||
verify "$1" || exit 1
|
||||
initctl -bq stop "pod:$1" || podman kill "$1"
|
||||
;;
|
||||
*)
|
||||
usage
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
@@ -34,7 +34,6 @@ BR2_LINUX_KERNEL_CUSTOM_DTS_OVERLAY="${BR2_EXTERNAL_INFIX_PATH}/board/aarch64/dt
|
||||
BR2_LINUX_KERNEL_DTB_KEEP_DIRNAME=y
|
||||
BR2_LINUX_KERNEL_INSTALL_TARGET=y
|
||||
BR2_PACKAGE_BUSYBOX_CONFIG="${BR2_EXTERNAL_INFIX_PATH}/board/common/busybox_defconfig"
|
||||
BR2_PACKAGE_BUSYBOX_SHOW_OTHERS=y
|
||||
BR2_PACKAGE_STRACE=y
|
||||
BR2_PACKAGE_STRESS_NG=y
|
||||
BR2_PACKAGE_JQ=y
|
||||
@@ -54,7 +53,7 @@ BR2_PACKAGE_UBOOT_TOOLS_FIT_SIGNATURE_SUPPORT=y
|
||||
BR2_PACKAGE_UBOOT_TOOLS_FIT_CHECK_SIGN=y
|
||||
BR2_PACKAGE_UBOOT_TOOLS_MKENVIMAGE=y
|
||||
BR2_PACKAGE_PYTHON3=y
|
||||
BR2_PACKAGE_CA_CERTIFICATES=y
|
||||
BR2_PACKAGE_LIBSSH_OPENSSL=y
|
||||
BR2_PACKAGE_LIBOPENSSL_BIN=y
|
||||
BR2_PACKAGE_LIBCURL_CURL=y
|
||||
BR2_PACKAGE_NETOPEER2_CLI=y
|
||||
@@ -71,7 +70,6 @@ BR2_PACKAGE_FPING=y
|
||||
BR2_PACKAGE_FRR=y
|
||||
# BR2_PACKAGE_IFUPDOWN_SCRIPTS is not set
|
||||
BR2_PACKAGE_IPROUTE2=y
|
||||
BR2_PACKAGE_IPTABLES=y
|
||||
BR2_PACKAGE_IPTABLES_NFTABLES=y
|
||||
BR2_PACKAGE_IPUTILS=y
|
||||
BR2_PACKAGE_LLDPD=y
|
||||
@@ -89,7 +87,6 @@ BR2_PACKAGE_SOCAT=y
|
||||
BR2_PACKAGE_TCPDUMP=y
|
||||
BR2_PACKAGE_TRACEROUTE=y
|
||||
BR2_PACKAGE_ULOGD=y
|
||||
BR2_PACKAGE_BASH=y
|
||||
BR2_PACKAGE_BASH_COMPLETION=y
|
||||
BR2_PACKAGE_SUDO=y
|
||||
BR2_PACKAGE_HTOP=y
|
||||
@@ -135,6 +132,10 @@ BR2_PACKAGE_IITO=y
|
||||
BR2_PACKAGE_KEYACK=y
|
||||
BR2_PACKAGE_KLISH_PLUGIN_INFIX=y
|
||||
BR2_PACKAGE_LOWDOWN=y
|
||||
BR2_PACKAGE_PODMAN=y
|
||||
BR2_PACKAGE_PODMAN_DRIVER_BTRFS=y
|
||||
BR2_PACKAGE_PODMAN_DRIVER_DEVICEMAPPER=y
|
||||
BR2_PACKAGE_PODMAN_DRIVER_VFS=y
|
||||
BR2_PACKAGE_TETRIS=y
|
||||
BR2_PACKAGE_QUERIERD=y
|
||||
BR2_PACKAGE_LIBINPUT=y
|
||||
|
||||
@@ -30,7 +30,6 @@ BR2_LINUX_KERNEL_CUSTOM_CONFIG_FILE="${BR2_EXTERNAL_INFIX_PATH}/board/x86_64/lin
|
||||
BR2_LINUX_KERNEL_INSTALL_TARGET=y
|
||||
BR2_LINUX_KERNEL_NEEDS_HOST_LIBELF=y
|
||||
BR2_PACKAGE_BUSYBOX_CONFIG="${BR2_EXTERNAL_INFIX_PATH}/board/common/busybox_defconfig"
|
||||
BR2_PACKAGE_BUSYBOX_SHOW_OTHERS=y
|
||||
BR2_PACKAGE_STRACE=y
|
||||
BR2_PACKAGE_STRESS_NG=y
|
||||
BR2_PACKAGE_JQ=y
|
||||
@@ -49,7 +48,7 @@ BR2_PACKAGE_UBOOT_TOOLS_FIT_SIGNATURE_SUPPORT=y
|
||||
BR2_PACKAGE_UBOOT_TOOLS_FIT_CHECK_SIGN=y
|
||||
BR2_PACKAGE_UBOOT_TOOLS_MKENVIMAGE=y
|
||||
BR2_PACKAGE_PYTHON3=y
|
||||
BR2_PACKAGE_CA_CERTIFICATES=y
|
||||
BR2_PACKAGE_LIBSSH_OPENSSL=y
|
||||
BR2_PACKAGE_LIBOPENSSL_BIN=y
|
||||
BR2_PACKAGE_LIBCURL_CURL=y
|
||||
BR2_PACKAGE_NETOPEER2_CLI=y
|
||||
@@ -66,7 +65,6 @@ BR2_PACKAGE_FPING=y
|
||||
BR2_PACKAGE_FRR=y
|
||||
# BR2_PACKAGE_IFUPDOWN_SCRIPTS is not set
|
||||
BR2_PACKAGE_IPROUTE2=y
|
||||
BR2_PACKAGE_IPTABLES=y
|
||||
BR2_PACKAGE_IPTABLES_NFTABLES=y
|
||||
BR2_PACKAGE_IPUTILS=y
|
||||
BR2_PACKAGE_LLDPD=y
|
||||
@@ -84,7 +82,6 @@ BR2_PACKAGE_SOCAT=y
|
||||
BR2_PACKAGE_TCPDUMP=y
|
||||
BR2_PACKAGE_TRACEROUTE=y
|
||||
BR2_PACKAGE_ULOGD=y
|
||||
BR2_PACKAGE_BASH=y
|
||||
BR2_PACKAGE_BASH_COMPLETION=y
|
||||
BR2_PACKAGE_SUDO=y
|
||||
BR2_PACKAGE_HTOP=y
|
||||
@@ -138,6 +135,10 @@ BR2_PACKAGE_IITO=y
|
||||
BR2_PACKAGE_KEYACK=y
|
||||
BR2_PACKAGE_KLISH_PLUGIN_INFIX=y
|
||||
BR2_PACKAGE_LOWDOWN=y
|
||||
BR2_PACKAGE_PODMAN=y
|
||||
BR2_PACKAGE_PODMAN_DRIVER_BTRFS=y
|
||||
BR2_PACKAGE_PODMAN_DRIVER_DEVICEMAPPER=y
|
||||
BR2_PACKAGE_PODMAN_DRIVER_VFS=y
|
||||
BR2_PACKAGE_TETRIS=y
|
||||
BR2_PACKAGE_QUERIERD=y
|
||||
GNS3_APPLIANCE_RAM=512
|
||||
|
||||
@@ -1,2 +1,5 @@
|
||||
d /run/containers/inbox 0700 - -
|
||||
d /run/containers/queue 0700 - -
|
||||
d /run/containers/done 0700 - -
|
||||
d /run/cni 0755 - -
|
||||
L+ /var/lib/cni - - - - /run/cni
|
||||
|
||||
@@ -219,7 +219,7 @@ sysrepoctl -s $SEARCH \
|
||||
-i infix-ip@2023-09-14.yang -g wheel -p 0660 \
|
||||
-i infix-if-type@2023-08-21.yang -g wheel -p 0660 \
|
||||
-i infix-routing@2024-01-09.yang -g wheel -p 0660 \
|
||||
-i infix-interfaces@2023-09-19.yang -g wheel -p 0660 \
|
||||
-i infix-interfaces@2024-01-15.yang -g wheel -p 0660 \
|
||||
-e vlan-filtering \
|
||||
-i ieee802-dot1ab-lldp@2022-03-15.yang -g wheel -p 0660 \
|
||||
-i infix-lldp@2023-08-23.yang -g wheel -p 0660 \
|
||||
|
||||
@@ -29,6 +29,7 @@ confd_plugin_la_SOURCES = \
|
||||
ietf-system.c \
|
||||
ietf-factory-default.c \
|
||||
ietf-routing.c \
|
||||
infix-containers.c \
|
||||
infix-dhcp.c \
|
||||
infix-services.c \
|
||||
infix-factory.c \
|
||||
|
||||
@@ -55,6 +55,9 @@ int core_post_hook(sr_session_ctx_t *session, uint32_t sub_id, const char *modul
|
||||
return SR_ERR_SYS;
|
||||
}
|
||||
|
||||
/* Everything done, including interfaces, launch all container scripts */
|
||||
infix_containers_launch();
|
||||
|
||||
/* skip reload in bootstrap, implicit reload in runlevel change */
|
||||
if (systemf("runlevel >/dev/null 2>&1"))
|
||||
return SR_ERR_OK;
|
||||
@@ -110,6 +113,9 @@ int sr_plugin_init_cb(sr_session_ctx_t *session, void **priv)
|
||||
if (rc)
|
||||
goto err;
|
||||
rc = ietf_system_init(&confd);
|
||||
if (rc)
|
||||
goto err;
|
||||
rc = infix_containers_init(&confd);
|
||||
if (rc)
|
||||
goto err;
|
||||
rc = infix_dhcp_init(&confd);
|
||||
|
||||
@@ -131,6 +131,10 @@ int ietf_interfaces_init(struct confd *confd);
|
||||
/* ietf-system.c */
|
||||
int ietf_system_init(struct confd *confd);
|
||||
|
||||
/* infix-containers.c */
|
||||
int infix_containers_init(struct confd *confd);
|
||||
void infix_containers_launch(void);
|
||||
|
||||
/* infix-dhcp.c */
|
||||
int infix_dhcp_init(struct confd *confd);
|
||||
|
||||
|
||||
@@ -26,6 +26,263 @@
|
||||
|
||||
#define IF_XPATH "/ietf-interfaces:interfaces/interface"
|
||||
|
||||
static bool iface_is_cni(const char *ifname, struct lyd_node *cif)
|
||||
{
|
||||
struct lyd_node *cni = lydx_get_child(cif, "container-network");
|
||||
|
||||
if (cni)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static void cni_gen_addrs(struct lyd_node *ip, FILE *fp, int *first)
|
||||
{
|
||||
struct lyd_node *addr;
|
||||
|
||||
if (!lydx_is_enabled(ip, "enabled"))
|
||||
return;
|
||||
|
||||
LYX_LIST_FOR_EACH(lyd_child(ip), addr, "address") {
|
||||
struct lyd_node *ip = lydx_get_child(addr, "ip");
|
||||
struct lyd_node *len = lydx_get_child(addr, "prefix-length");
|
||||
|
||||
if (*first)
|
||||
fprintf(fp, ",\n \"addresses\": [\n");
|
||||
|
||||
fprintf(fp, "%s { \"address\": \"%s/%s\" }",
|
||||
*first ? "" : ",\n", lyd_get_value(ip), lyd_get_value(len));
|
||||
*first = 0;
|
||||
}
|
||||
}
|
||||
|
||||
#if 0 /* Unused for now, use container specific global dns and search settings instead. */
|
||||
static void cni_gen_dns(struct lyd_node *cni, FILE *fp, int *first)
|
||||
{
|
||||
struct lyd_node *dns;
|
||||
|
||||
dns = lydx_get_child(cni, "dns");
|
||||
if (dns) {
|
||||
struct lyd_node *node;
|
||||
|
||||
puts("Adding DNS to CNI profile!");
|
||||
fprintf(fp, ",\n \"dns\": {");
|
||||
|
||||
*first = 1;
|
||||
LYX_LIST_FOR_EACH(lyd_child(dns), node, "nameservers") {
|
||||
if (*first)
|
||||
fprintf(fp, "\n \"nameservers\": [ ");
|
||||
else
|
||||
fprintf(fp, ", ");
|
||||
|
||||
printf("Adding DNS nameserver %s to CNI profile!\n", lyd_get_value(node));
|
||||
fprintf(fp, "\"%s\"", lyd_get_value(node));
|
||||
(*first)++;
|
||||
}
|
||||
if (*first > 1)
|
||||
fprintf(fp, " ]");
|
||||
|
||||
node = lydx_get_child(dns, "domain");
|
||||
if (node) {
|
||||
fprintf(fp, "%s\n \"domain\": \"%s\"",
|
||||
*first > 1 ? "," : "", lyd_get_value(node));
|
||||
(*first)++;
|
||||
}
|
||||
|
||||
LYX_LIST_FOR_EACH(lyd_child(cni), node, "search") {
|
||||
if (*first)
|
||||
fprintf(fp, "%s\n \"search\": [ ", *first > 1 ? "," : "");
|
||||
else
|
||||
fprintf(fp, ", ");
|
||||
|
||||
fprintf(fp, "\"%s\"", lyd_get_value(node));
|
||||
*first = 0;
|
||||
}
|
||||
|
||||
fprintf(fp, "%s\n }", *first ? "" : "]");
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Set up IP masquerading bridge which acts as a gateway for nodes behind it.
|
||||
* Default subnet, if one is missing in configuration, is: 10.88.0.0/16
|
||||
*/
|
||||
static int cni_bridge(struct lyd_node *cni, const char *ifname)
|
||||
{
|
||||
struct lyd_node *net;
|
||||
int first = 1;
|
||||
FILE *fp;
|
||||
|
||||
fp = fopenf("w", "/etc/cni/net.d/90-%s-bridge.conflist", ifname);
|
||||
if (!fp)
|
||||
return -EIO;
|
||||
|
||||
fprintf(fp, "{\n"
|
||||
" \"cniVersion\": \"1.0.0\",\n"
|
||||
" \"name\": \"%s\",\n"
|
||||
" \"plugins\": [\n"
|
||||
" {\n"
|
||||
" \"type\": \"bridge\",\n"
|
||||
" \"bridge\": \"%s\",\n"
|
||||
" \"isGateway\": true,\n"
|
||||
" \"ipMasq\": true,\n"
|
||||
" \"hairpinMode\": true,\n"
|
||||
// " \"dataDir\": \"/run/containers/networks\",\n"
|
||||
" \"ipam\": {\n"
|
||||
" \"type\": \"host-local\"", ifname, ifname);
|
||||
|
||||
LYX_LIST_FOR_EACH(lyd_child(cni), net, "route") {
|
||||
struct lyd_node *subnet = lydx_get_child(net, "subnet");
|
||||
struct lyd_node *gateway = lydx_get_child(net, "gateway");
|
||||
|
||||
if (first)
|
||||
fprintf(fp, ",\n \"routes\": [\n");
|
||||
else
|
||||
fprintf(fp, ",\n");
|
||||
|
||||
fprintf(fp, " {\n"
|
||||
" \"dst\": \"%s\"%s\n", lyd_get_value(subnet), gateway ? "," : "");
|
||||
if (gateway)
|
||||
fprintf(fp, " \"gw\": \"%s\"\n", lyd_get_value(gateway));
|
||||
fprintf(fp, " }");
|
||||
|
||||
first = 0;
|
||||
}
|
||||
if (!first)
|
||||
fprintf(fp, " ]");
|
||||
else
|
||||
fprintf(fp, ",\n \"routes\": [ { \"dst\": \"0.0.0.0/0\" } ]");
|
||||
|
||||
first = 1;
|
||||
LYX_LIST_FOR_EACH(lyd_child(cni), net, "subnet") {
|
||||
struct lyd_node *subnet = lydx_get_child(net, "subnet");
|
||||
struct lyd_node *gateway = lydx_get_child(net, "gateway");
|
||||
|
||||
if (first)
|
||||
fprintf(fp, ",\n \"ranges\": [\n");
|
||||
else
|
||||
fprintf(fp, ",\n");
|
||||
|
||||
fprintf(fp, " [{\n"
|
||||
" \"subnet\": \"%s\"%s\n", lyd_get_value(subnet), gateway ? "," : "");
|
||||
if (gateway)
|
||||
fprintf(fp, " \"gateway\": \"%s\"\n", lyd_get_value(gateway));
|
||||
fprintf(fp, " }]");
|
||||
|
||||
first = 0;
|
||||
}
|
||||
if (!first)
|
||||
fprintf(fp, " ]");
|
||||
else
|
||||
/* Default is a customary docker0 local network */
|
||||
fprintf(fp, ",\n \"ranges\": [ [{ \"subnet\": \"172.17.0.0/16\" }] ]");
|
||||
|
||||
fprintf(fp,
|
||||
"\n }\n" /* /ipam */
|
||||
" },\n" /* /bridge */
|
||||
" {\n"
|
||||
" \"type\": \"portmap\",\n"
|
||||
" \"capabilities\": {\n"
|
||||
" \"portMappings\": true\n"
|
||||
" }\n"
|
||||
" },\n" /* /portmap */
|
||||
" {\n"
|
||||
" \"type\": \"firewall\"\n"
|
||||
" },\n" /* /firewall */
|
||||
" {\n"
|
||||
" \"type\": \"tuning\"\n"
|
||||
" }\n" /* /tuning */
|
||||
" ]\n"
|
||||
"}\n");
|
||||
|
||||
if (fclose(fp))
|
||||
return -errno;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int cni_host(struct lyd_node *cni, const char *ifname)
|
||||
{
|
||||
struct lyd_node *net, *ip;
|
||||
int first = 1;
|
||||
FILE *fp;
|
||||
|
||||
fp = fopenf("w", "/etc/cni/net.d/90-%s-host.conflist", ifname);
|
||||
if (!fp)
|
||||
return -EIO;
|
||||
|
||||
fprintf(fp, "{\n"
|
||||
" \"cniVersion\": \"1.0.0\",\n"
|
||||
" \"name\": \"%s\",\n"
|
||||
" \"plugins\": [\n"
|
||||
" {\n"
|
||||
" \"type\": \"host-device\",\n"
|
||||
" \"device\": \"%s\",\n"
|
||||
" \"ipam\": {\n"
|
||||
" \"type\": \"static\"", ifname, ifname);
|
||||
|
||||
|
||||
ip = lydx_get_child(lyd_parent(cni), "ipv4");
|
||||
if (ip)
|
||||
cni_gen_addrs(ip, fp, &first);
|
||||
|
||||
ip = lydx_get_child(lyd_parent(cni), "ipv6");
|
||||
if (ip)
|
||||
cni_gen_addrs(ip, fp, &first);
|
||||
|
||||
if (!first)
|
||||
fprintf(fp, "\n ]");
|
||||
|
||||
first = 1;
|
||||
LYX_LIST_FOR_EACH(lyd_child(cni), net, "route") {
|
||||
struct lyd_node *subnet = lydx_get_child(net, "subnet");
|
||||
struct lyd_node *gateway = lydx_get_child(net, "gateway");
|
||||
|
||||
if (first)
|
||||
fprintf(fp, ",\n \"routes\": [\n");
|
||||
else
|
||||
fprintf(fp, ",\n");
|
||||
|
||||
fprintf(fp, " {\n"
|
||||
" \"dst\": \"%s\"%s\n", lyd_get_value(subnet), gateway ? "," : "");
|
||||
if (gateway)
|
||||
fprintf(fp, " \"gw\": \"%s\"\n", lyd_get_value(gateway));
|
||||
fprintf(fp, " }");
|
||||
|
||||
first = 0;
|
||||
}
|
||||
if (!first)
|
||||
fprintf(fp, " ]");
|
||||
|
||||
fprintf(fp,
|
||||
"\n }\n"
|
||||
" }\n"
|
||||
" ]\n"
|
||||
"}\n");
|
||||
|
||||
if (fclose(fp))
|
||||
return -errno;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int iface_gen_cni(const char *ifname, struct lyd_node *cif)
|
||||
{
|
||||
struct lyd_node *cni = lydx_get_child(cif, "container-network");
|
||||
const char *type = lydx_get_cattr(cni, "type");
|
||||
|
||||
ERROR("Got CNI %s iface %s", type, ifname);
|
||||
if (!strcmp(type, "cni-host"))
|
||||
return cni_host(cni, ifname);
|
||||
|
||||
if (!strcmp(type, "cni-bridge"))
|
||||
return cni_bridge(cni, ifname);
|
||||
|
||||
ERROR("Unknown CNI interface type %s, skipping.", type);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static bool iface_is_phys(const char *ifname)
|
||||
{
|
||||
bool is_phys = false;
|
||||
@@ -1182,6 +1439,11 @@ static sr_error_t netdag_gen_iface(struct dagger *net,
|
||||
op = LYDX_OP_CREATE;
|
||||
}
|
||||
|
||||
if (iface_is_cni(ifname, cif)) {
|
||||
err = iface_gen_cni(ifname, cif);
|
||||
goto err;
|
||||
}
|
||||
|
||||
ip = dagger_fopen_next(net, "init", ifname, 50, "init.ip");
|
||||
if (!ip) {
|
||||
err = -EIO;
|
||||
|
||||
@@ -0,0 +1,213 @@
|
||||
/* SPDX-License-Identifier: BSD-3-Clause */
|
||||
|
||||
#include <assert.h>
|
||||
#include <ctype.h>
|
||||
#include <dirent.h>
|
||||
#include <pwd.h>
|
||||
#include <sys/utsname.h>
|
||||
#include <sys/sysinfo.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#include <srx/common.h>
|
||||
#include <srx/lyx.h>
|
||||
#include <srx/srx_module.h>
|
||||
#include <srx/srx_val.h>
|
||||
|
||||
#include "core.h"
|
||||
#define ARPING_MSEC 1000
|
||||
#define MODULE "infix-containers"
|
||||
#define CFG_XPATH "/infix-containers:container"
|
||||
#define INBOX_QUEUE "/run/containers/inbox"
|
||||
#define JOB_QUEUE "/run/containers/queue"
|
||||
#define LOGGER "logger -t container -p local1.notice"
|
||||
|
||||
static const struct srx_module_requirement reqs[] = {
|
||||
{ .dir = YANG_PATH_, .name = MODULE, .rev = "2023-12-14" },
|
||||
{ NULL }
|
||||
};
|
||||
|
||||
static int job(const char *name, struct lyd_node *cif)
|
||||
{
|
||||
const char *image = lydx_get_cattr(cif, "image");
|
||||
struct lyd_node *net;
|
||||
FILE *fp;
|
||||
|
||||
fp = fopenf("w", "%s/%s.sh", INBOX_QUEUE, name);
|
||||
if (!fp) {
|
||||
ERROR("Failed adding job %s.sh to job queue" INBOX_QUEUE, name);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Stop any running container gracefully so it releases its IP addresses. */
|
||||
fprintf(fp, "#!/bin/sh\n"
|
||||
"container stop %s\n"
|
||||
"container ", name);
|
||||
|
||||
LYX_LIST_FOR_EACH(lyd_child(cif), net, "dns")
|
||||
fprintf(fp, "--dns %s ", lyd_get_value(net));
|
||||
|
||||
LYX_LIST_FOR_EACH(lyd_child(cif), net, "search")
|
||||
fprintf(fp, "--dns-search %s ", lyd_get_value(net));
|
||||
|
||||
fprintf(fp, "create %s %s", name, image);
|
||||
|
||||
LYX_LIST_FOR_EACH(lyd_child(cif), net, "network") {
|
||||
struct lyd_node *opt;
|
||||
const char *name;
|
||||
int first = 1;
|
||||
|
||||
name = lydx_get_cattr(net, "name");
|
||||
fprintf(fp, " %s", name);
|
||||
LYX_LIST_FOR_EACH(lyd_child(net), opt, "option") {
|
||||
const char *option = lyd_get_value(opt);
|
||||
|
||||
fprintf(fp, "%s%s", first ? ":" : ",", option);
|
||||
first = 0;
|
||||
}
|
||||
}
|
||||
|
||||
fprintf(fp, "\n");
|
||||
fchmod(fileno(fp), 0700);
|
||||
|
||||
return fclose(fp);
|
||||
}
|
||||
|
||||
static int add(const char *name, struct lyd_node *cif)
|
||||
{
|
||||
FILE *fp;
|
||||
|
||||
if (job(name, cif))
|
||||
return SR_ERR_SYS;
|
||||
|
||||
fp = fopenf("w", "/etc/finit.d/available/pod:%s.conf", name);
|
||||
if (!fp) {
|
||||
ERROR("Failed creating container %s monitor", name);
|
||||
return SR_ERR_SYS;
|
||||
}
|
||||
|
||||
fprintf(fp, "service name:pod log:prio:local1.err,tag:container :%s pid:!/run/pod:%s.pid \\\n"
|
||||
" [2345] <usr/pod:%s> podman start -a %s -- Container %s\n",
|
||||
name, name, name, name, name);
|
||||
fclose(fp);
|
||||
|
||||
if (systemf("initctl -nbq enable pod:%s", name)) {
|
||||
ERROR("Failed enabling container %s monitor", name);
|
||||
return SR_ERR_SYS;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int del(const char *name)
|
||||
{
|
||||
char fn[strlen(JOB_QUEUE) + strlen(name) + 5];
|
||||
|
||||
/* Remove any pending download/create job first */
|
||||
snprintf(fn, sizeof(fn), "%s/%s.sh", JOB_QUEUE, name);
|
||||
erase(fn);
|
||||
snprintf(fn, sizeof(fn), "%s/%s.sh", INBOX_QUEUE, name);
|
||||
erase(fn);
|
||||
|
||||
return systemf("container delete %s", name);
|
||||
}
|
||||
|
||||
static int change(sr_session_ctx_t *session, uint32_t sub_id, const char *module,
|
||||
const char *xpath, sr_event_t event, unsigned request_id, void *_confd)
|
||||
{
|
||||
struct lyd_node *diff, *cifs, *difs, *cif, *dif;
|
||||
sr_error_t err = 0;
|
||||
sr_data_t *cfg;
|
||||
|
||||
switch (event) {
|
||||
case SR_EV_DONE:
|
||||
break;
|
||||
case SR_EV_CHANGE:
|
||||
case SR_EV_ABORT:
|
||||
default:
|
||||
return SR_ERR_OK;
|
||||
}
|
||||
|
||||
err = sr_get_data(session, CFG_XPATH "//.", 0, 0, 0, &cfg);
|
||||
if (err)
|
||||
goto err_abandon;
|
||||
|
||||
err = srx_get_diff(session, &diff);
|
||||
if (err)
|
||||
goto err_release_data;
|
||||
|
||||
cifs = lydx_get_descendant(cfg->tree, "container", "container", NULL);
|
||||
difs = lydx_get_descendant(diff, "container", "container", NULL);
|
||||
|
||||
/* find the modified one, delete or recreate only that */
|
||||
LYX_LIST_FOR_EACH(difs, dif, "container") {
|
||||
const char *name = lydx_get_cattr(dif, "name");
|
||||
|
||||
ERROR("Change in container %s", name);
|
||||
if (lydx_get_op(dif) == LYDX_OP_DELETE) {
|
||||
ERROR("OP DELETE container %s", name);
|
||||
del(name);
|
||||
continue;
|
||||
}
|
||||
|
||||
LYX_LIST_FOR_EACH(cifs, cif, "container") {
|
||||
const char *nm = lydx_get_cattr(cif, "name");
|
||||
|
||||
ERROR("container %s vs %s", name, nm);
|
||||
if (strcmp(name, nm)) {
|
||||
ERROR("Skipping container %s", nm);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!lydx_is_enabled(cif, "enabled")) {
|
||||
ERROR("container %s not enabled", nm);
|
||||
del(name);
|
||||
} else {
|
||||
ERROR("container %s enabled", nm);
|
||||
add(name, cif);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
lyd_free_tree(diff);
|
||||
err_release_data:
|
||||
sr_release_data(cfg);
|
||||
err_abandon:
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
void infix_containers_launch(void)
|
||||
{
|
||||
struct dirent *d;
|
||||
DIR *dir;
|
||||
|
||||
dir = opendir(INBOX_QUEUE);
|
||||
if (!dir) {
|
||||
ERROR("Cannot open %s to launch scripts.", INBOX_QUEUE);
|
||||
return;
|
||||
}
|
||||
|
||||
while ((d = readdir(dir))) {
|
||||
char fn[strlen(INBOX_QUEUE) + strlen(d->d_name) + 2];
|
||||
|
||||
snprintf(fn, sizeof(fn), "%s/%s", INBOX_QUEUE, d->d_name);
|
||||
if (movefile(fn, JOB_QUEUE))
|
||||
ERRNO("Failed moving %s to job queue %s", fn, JOB_QUEUE);
|
||||
}
|
||||
}
|
||||
|
||||
int infix_containers_init(struct confd *confd)
|
||||
{
|
||||
int rc;
|
||||
|
||||
rc = srx_require_modules(confd->conn, reqs);
|
||||
if (rc)
|
||||
goto fail;
|
||||
|
||||
REGISTER_CHANGE(confd->session, MODULE, CFG_XPATH, 0, change, confd, &confd->sub);
|
||||
return SR_ERR_OK;
|
||||
fail:
|
||||
ERROR("init failed: %s", sr_strerror(rc));
|
||||
return rc;
|
||||
}
|
||||
@@ -0,0 +1,186 @@
|
||||
/*
|
||||
* Infix Containers YANG module
|
||||
*/
|
||||
module infix-containers {
|
||||
yang-version 1.1;
|
||||
namespace "urn:ietf:params:xml:ns:yang:infix-containers";
|
||||
prefix infix-cont;
|
||||
|
||||
import ietf-yang-types {
|
||||
prefix yang;
|
||||
}
|
||||
|
||||
import ietf-interfaces {
|
||||
prefix if;
|
||||
}
|
||||
|
||||
import ietf-inet-types {
|
||||
prefix inet;
|
||||
}
|
||||
|
||||
import infix-interfaces {
|
||||
prefix infix-if;
|
||||
}
|
||||
|
||||
revision 2023-12-14 {
|
||||
description "Initial revision";
|
||||
reference "internal";
|
||||
}
|
||||
|
||||
/*
|
||||
* Typedefs
|
||||
*/
|
||||
|
||||
typedef restart-policy {
|
||||
type enumeration {
|
||||
enum no {
|
||||
description "Do not restart containers on exit.";
|
||||
value 1;
|
||||
}
|
||||
enum always {
|
||||
description "Restart containers when they exit, regardless of status.";
|
||||
value 2;
|
||||
}
|
||||
enum on-failure {
|
||||
description "Restart containers when they exit with a non-0 exit code.";
|
||||
value 3;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Data Nodes
|
||||
*/
|
||||
|
||||
container container {
|
||||
list container {
|
||||
key "name";
|
||||
|
||||
leaf name {
|
||||
description "Name of the Docker container";
|
||||
type string;
|
||||
}
|
||||
|
||||
leaf enabled {
|
||||
description "Enable or disable a container configuration.";
|
||||
type boolean;
|
||||
default true;
|
||||
}
|
||||
|
||||
leaf image {
|
||||
description "Docker image used for the container";
|
||||
mandatory true;
|
||||
type string;
|
||||
}
|
||||
|
||||
leaf restart {
|
||||
description "Restart policy to follow when containers exit.";
|
||||
type restart-policy;
|
||||
default no;
|
||||
}
|
||||
|
||||
leaf manual {
|
||||
description "Auto-start or manual start after creation/reboot.";
|
||||
type boolean;
|
||||
}
|
||||
|
||||
choice network {
|
||||
description "Select network mode: none, host, or container network interfaces.";
|
||||
|
||||
case host {
|
||||
leaf host {
|
||||
description "Run as host container, with full access to all network interfaces.";
|
||||
type boolean;
|
||||
}
|
||||
}
|
||||
|
||||
case network {
|
||||
list network {
|
||||
key name;
|
||||
|
||||
leaf name {
|
||||
description "CNI network to connect to the container.";
|
||||
type if:interface-ref;
|
||||
must "/if:interfaces/if:interface[if:name = current()]/infix-if:container-network" {
|
||||
error-message "Container networks must be interfaces classified as container-network.";
|
||||
}
|
||||
}
|
||||
|
||||
leaf-list option {
|
||||
description "Options for CNI bridges.
|
||||
Example: ip=1.2.3.4 to request a specific IP, both IPv4 and IPv6.
|
||||
interface_name=foo0 name to set interface name inside container.";
|
||||
type string;
|
||||
}
|
||||
}
|
||||
|
||||
leaf-list publish {
|
||||
description "Publish container port, or a range of ports, to the host.";
|
||||
type string;
|
||||
}
|
||||
|
||||
leaf-list dns {
|
||||
description "Set custom DNS servers, or 'none' to use /etc/resolv.conf in image.";
|
||||
type string;
|
||||
}
|
||||
|
||||
leaf-list search {
|
||||
description "Set custom DNS search domains, or '.' to not set search domain.";
|
||||
type string;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
container statistics {
|
||||
description "Statistics of the container";
|
||||
config false;
|
||||
|
||||
leaf status {
|
||||
description "Status of the Docker container";
|
||||
type enumeration {
|
||||
enum running;
|
||||
enum stopped;
|
||||
}
|
||||
}
|
||||
|
||||
leaf created {
|
||||
description "Creation timestamp of the container";
|
||||
type yang:date-and-time;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
rpc container-pull {
|
||||
description "Upgrade (fetch) a container image";
|
||||
|
||||
input {
|
||||
leaf image {
|
||||
description "Name of the container image to upgrade.";
|
||||
type string;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
rpc container-stop {
|
||||
description "Stop a container";
|
||||
|
||||
input {
|
||||
leaf name {
|
||||
description "Name of the container to stop";
|
||||
type string;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
rpc container-start {
|
||||
description "Start a container";
|
||||
|
||||
input {
|
||||
leaf name {
|
||||
description "Name of the container to start";
|
||||
type string;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,107 @@
|
||||
submodule infix-if-container {
|
||||
yang-version 1.1;
|
||||
belongs-to infix-interfaces {
|
||||
prefix infix-if;
|
||||
}
|
||||
|
||||
import ietf-inet-types {
|
||||
prefix inet;
|
||||
}
|
||||
import ietf-interfaces {
|
||||
prefix if;
|
||||
}
|
||||
import infix-if-type {
|
||||
prefix infixift;
|
||||
}
|
||||
|
||||
organization "KernelKit";
|
||||
contact "kernelkit@googlegroups.com";
|
||||
description "CNI network to interface mapper extension for ietf-interfaces.
|
||||
This ensures a container interface can never be a bridge port,
|
||||
or LAG member at the same time.";
|
||||
|
||||
revision 2024-01-15 {
|
||||
description "Initial revision.";
|
||||
reference "internal";
|
||||
}
|
||||
|
||||
/*
|
||||
* Identities
|
||||
*/
|
||||
|
||||
identity cni-type {
|
||||
description "CNI network type";
|
||||
}
|
||||
|
||||
identity cni-bridge {
|
||||
base cni-type;
|
||||
description "CNI standard bridge with IP masquerading, portmappping, and firewalling.";
|
||||
}
|
||||
|
||||
identity cni-host {
|
||||
base cni-type;
|
||||
description "CNI host-device, e.g., one end of a VETH pair or other host interface.";
|
||||
}
|
||||
|
||||
/*
|
||||
* Data Nodes
|
||||
*/
|
||||
|
||||
augment "/if:interfaces/if:interface/infix-if:port" {
|
||||
description "Augments the interface model with CNI networks.";
|
||||
|
||||
case container-network {
|
||||
container container-network {
|
||||
presence "Container network (CNI) mapping.";
|
||||
|
||||
leaf type {
|
||||
description "Bridge or host interface";
|
||||
type identityref {
|
||||
base cni-type;
|
||||
}
|
||||
default cni-host;
|
||||
}
|
||||
|
||||
list subnet {
|
||||
description "Static IP ranges to hand out addresses to containers from.
|
||||
A cni-bridge will forward DNS, NTP, and SSH by default to
|
||||
the host interfaces.";
|
||||
when "../type = 'infix-if:cni-bridge'";
|
||||
key subnet;
|
||||
|
||||
leaf subnet {
|
||||
type inet:ip-prefix;
|
||||
description "Subnet to assign addresses from, round-robin assignment.
|
||||
The default is from a standard Docker setup.";
|
||||
default "172.17.0.0/16";
|
||||
}
|
||||
|
||||
leaf gateway {
|
||||
type inet:ip-address;
|
||||
description "Optional gateway address for the subnet, defaults to .1.
|
||||
This will be used as the address of a cni-bridge.";
|
||||
}
|
||||
}
|
||||
|
||||
list route {
|
||||
description "IPv4 or IPv6 routes to be added to container.
|
||||
|
||||
For cni-bridge type interfaces the gateway can be omitted,
|
||||
the the IP address of the CNI bridge will then be used as
|
||||
the next-hop address.";
|
||||
key subnet;
|
||||
|
||||
leaf subnet {
|
||||
type inet:ip-prefix;
|
||||
description "Destination (subnet) IP address.";
|
||||
}
|
||||
|
||||
leaf gateway {
|
||||
type inet:ip-address;
|
||||
description "Optional gateway (next-hop) IP address for the route.";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+6
@@ -12,6 +12,7 @@ module infix-interfaces {
|
||||
|
||||
include infix-if-base;
|
||||
include infix-if-bridge;
|
||||
include infix-if-container;
|
||||
include infix-if-veth;
|
||||
include infix-if-vlan;
|
||||
|
||||
@@ -19,6 +20,11 @@ module infix-interfaces {
|
||||
contact "kernelkit@googlegroups.com";
|
||||
description "Linux bridge and lag extensions for ietf-interfaces.";
|
||||
|
||||
revision 2024-01-15 {
|
||||
description "Add support for container ports (CNI networks).";
|
||||
reference "internal";
|
||||
}
|
||||
|
||||
revision 2023-09-19 {
|
||||
description "Add deviation to allow setting phys-address on links.";
|
||||
reference "internal";
|
||||
@@ -113,6 +113,27 @@
|
||||
<ACTION sym="STRING"/>
|
||||
</PTYPE>
|
||||
|
||||
<PTYPE name="CONTAINERS">
|
||||
<COMPL>
|
||||
<ACTION sym="script">container list</ACTION>
|
||||
</COMPL>
|
||||
<ACTION sym="STRING"/>
|
||||
</PTYPE>
|
||||
|
||||
<PTYPE name="CONTAINERSa">
|
||||
<COMPL>
|
||||
<ACTION sym="script">container -a list</ACTION>
|
||||
</COMPL>
|
||||
<ACTION sym="STRING"/>
|
||||
</PTYPE>
|
||||
|
||||
<PTYPE name="IMAGES">
|
||||
<COMPL>
|
||||
<ACTION sym="script">container -a list images</ACTION>
|
||||
</COMPL>
|
||||
<ACTION sym="STRING"/>
|
||||
</PTYPE>
|
||||
|
||||
<VIEW name="main">
|
||||
<HOTKEY key="^D" cmd="exit"/>
|
||||
|
||||
@@ -162,6 +183,65 @@
|
||||
<ACTION sym="nav">replace config</ACTION>
|
||||
</COMMAND>
|
||||
|
||||
<COMMAND name="container" help="Manage containers and their images" mode="switch">
|
||||
<COMMAND name="attach" help="Attach to a running container (exec sh)">
|
||||
<PARAM name="name" ptype="/CONTAINERS" help="Container name" />
|
||||
<SWITCH name="optional" min="0">
|
||||
<COMMAND name="command" help="Command to run">
|
||||
<PARAM name="command" ptype="/STRING" help="Command to run2"/>
|
||||
</COMMAND>
|
||||
</SWITCH>
|
||||
<ACTION sym="script" in="tty" out="tty" interrupt="true">
|
||||
if [ -z "$KLISH_PARAM_name" ]; then
|
||||
echo "Missing container name."
|
||||
else
|
||||
cmd=${KLISH_PARAM_command:-sh}
|
||||
container attach $KLISH_PARAM_name $cmd
|
||||
fi
|
||||
</ACTION>
|
||||
</COMMAND>
|
||||
|
||||
<COMMAND name="pull" help="Fetch a container image">
|
||||
<PARAM name="image" ptype="/STRING" help="Image url and tag, e.g. docker://hello-world:latest" />
|
||||
<SWITCH name="optional" min="0">
|
||||
<COMMAND name="creds" help="Credentials">
|
||||
<PARAM name="creds" ptype="/STRING" help="[username[:password]]"/>
|
||||
</COMMAND>
|
||||
</SWITCH>
|
||||
<ACTION sym="script" out="tty" interrupt="true">
|
||||
creds==${KLISH_PARAM_creds:+--creds=$KLISH_PARAM_creds}
|
||||
container pull $creds $KLISH_PARAM_image
|
||||
</ACTION>
|
||||
</COMMAND>
|
||||
|
||||
<COMMAND name="remove" help="Remove a container image from local storage">
|
||||
<PARAM name="name" ptype="/IMAGES" help="Image name" />
|
||||
<ACTION sym="script" out="tty" interrupt="true">container remove $KLISH_PARAM_name</ACTION>
|
||||
</COMMAND>
|
||||
|
||||
<COMMAND name="run" help="Run a container image, with optional command">
|
||||
<PARAM name="image" ptype="/IMAGES" help="Image name" />
|
||||
<SWITCH name="optional" min="0">
|
||||
<COMMAND name="command" help="Command to run">
|
||||
<PARAM name="command" ptype="/STRING" help="Command to run2"/>
|
||||
</COMMAND>
|
||||
</SWITCH>
|
||||
<ACTION sym="script" out="tty" interrupt="true">
|
||||
container run $KLISH_PARAM_image ${KLISH_PARAM_command}
|
||||
</ACTION>
|
||||
</COMMAND>
|
||||
|
||||
<COMMAND name="start" help="Start a container">
|
||||
<PARAM name="name" ptype="/CONTAINERSa" help="Container name" />
|
||||
<ACTION sym="script" out="tty" interrupt="true">container start $KLISH_PARAM_name</ACTION>
|
||||
</COMMAND>
|
||||
|
||||
<COMMAND name="stop" help="Stop a container">
|
||||
<PARAM name="name" ptype="/CONTAINERS" help="Container name" />
|
||||
<ACTION sym="script" out="tty" interrupt="true">container stop $KLISH_PARAM_name</ACTION>
|
||||
</COMMAND>
|
||||
</COMMAND>
|
||||
|
||||
<COMMAND name="copy" help="Copy configuration, e.g., copy running-config startup-config">
|
||||
<PARAM name="src" ptype="/DATASTORE" help="Source datastore"/>
|
||||
<PARAM name="dst" ptype="/RW_DATASTORE" help="Destination datastore"/>
|
||||
@@ -241,6 +321,27 @@
|
||||
</SWITCH>
|
||||
</COMMAND>
|
||||
|
||||
<COMMAND name="container" help="Show container status">
|
||||
<ACTION sym="script">podman ps</ACTION>
|
||||
|
||||
<SWITCH name="optional" min="0">
|
||||
<COMMAND name="images" help="Show container images">
|
||||
<ACTION sym="script">podman images</ACTION>
|
||||
<SWITCH name="optional" min="0">
|
||||
<COMMAND name="all" help="All images (default hides intermediate imaages)">
|
||||
<ACTION sym="script">podman images -a</ACTION>
|
||||
</COMMAND>
|
||||
</SWITCH>
|
||||
</COMMAND>
|
||||
<COMMAND name="all" help="Show all containers (default only running)">
|
||||
<ACTION sym="script">podman ps -a</ACTION>
|
||||
</COMMAND>
|
||||
<COMMAND name="log" help="Show container log (alias for show log container)">
|
||||
<ACTION sym="script">cat /log/container</ACTION>
|
||||
</COMMAND>
|
||||
</SWITCH>
|
||||
</COMMAND>
|
||||
|
||||
<COMMAND name="datetime" help="Show current date and time, default RFC2822 format">
|
||||
<ACTION sym="script">date -R</ACTION>
|
||||
<SWITCH name="optional" min="0">
|
||||
|
||||
Reference in New Issue
Block a user