wmdeit-cf-wmdelib/lxc-host.cf

471 lines
14 KiB
CFEngine3

# example lxc_host.cfg
# cfg data => '
# {
# "bridge": "lxcbr0",
# "netmask": "255.255.255.0",
# "network": "10.0.1.0/24",
# "addr": "10.0.1.1",
# "dhcp_range": "10.0.1.200,10.0.1.254"
# "dhcp_max": 253,
# }';
bundle agent lxc_host( cfg )
{
vars:
# var ip wird in mergedata verwendet, und mergedata mag keine flachen Variablen,
# sondern braucht Arrays auf die eine oder andere Weise
'ip[thirdoctet]' string => format( "%02x", nth( splitstring( $(cfg[addr]), '\.', 4 ), 2 ) );
"cfg_ip" data => mergedata( cfg, ip );
debian|ubuntu::
'lxc_path' string => '/etc/lxc';
'lxc_net_path' string => '/etc/default/lxc-net';
'lxc_net_tmpl' string => 'debian.lxc-net.mustache';
'lxc_default_path' string => '$(lxc_path)/default.conf';
'lxc_default_tmpl' string => 'default.conf.mustache';
'lxc_hosts_d' string => '$(lxc_path)/hosts.d';
debian::
'pkg_list' slist => { 'lxc','lxc-templates','debian-archive-keyring' };
'lxc_nft_path' string => '/usr/local/bin';
'lxc_down_nft' string => '$(sys.workdir)/inputs/$(def.wmde_libdir)/templates/lxc_host/lxc-net-down.nft.txt';
'lxc_net_override' string => '$(sys.workdir)/inputs/$(def.wmde_libdir)/templates/lxc_host/lxc-net.override.conf';
debian_11::
'lxc_up_nft' string => '$(sys.workdir)/inputs/$(def.wmde_libdir)/templates/lxc_host/debian11.lxc-net-up.nft.txt';
'lxc_hook_net' string => '$(sys.workdir)/inputs/$(def.wmde_libdir)/scripts/lxc_host/debian11.lxc-hook-net.sh';
debian_12|debian_13::
'lxc_up_nft' string => '$(sys.workdir)/inputs/$(def.wmde_libdir)/templates/lxc_host/debian12.lxc-net-up.nft.txt';
'lxc_hook_net' string => '$(sys.workdir)/inputs/$(def.wmde_libdir)/scripts/lxc_host/debian12.lxc-hook-net.sh';
ubuntu::
'pkg_list' slist => { 'lxc','lxc-templates','ubuntu-archive-keyring' };
fedora|centos|redhat::
'lxc_net_path' string => '/etc/lxc-net';
methods:
'Ensure the packages required for LXC are installed'
usebundle => wmde_install_packages(@(pkg_list),'lxc'),
comment => "The packages to install are defined in var `pkg_list`.",
handle => "lxc_installed";
reports:
"The agent $(default:def.agent_name) has been prepared as a host for lx containers.";
"The host provides the network on $(cfg[bridge]) with address $(cfg[addr]).";
"It will configure LXC via DHCP in the range $(cfg[dhcp_range]).";
"Static mappings are in /etc/lxc/hosts.d.";
"This is the third octet: $(ip[thirdoctet]).";
files:
"$(lxc_path)/."
acl => lxc_dnsmasq,
depends_on => { "lxc_installed" };
"$(lxc_path)/dnsmasq.conf"
perms => mog('644','root','root'),
content => "dhcp-hostsdir=$(lxc_hosts_d)",
depends_on => { "lxc_installed" };
"$(lxc_hosts_d)/."
create => "true",
perms => mog('755','root','root'),
handle => "lxchostsdir",
depends_on => { "lxc_installed" };
"/usr/local/bin/lxc-hooks"
perms => mog('700','root','root'),
copy_from => local_cp("$(sys.workdir)/inputs/wmdelib/scripts/lxc_host/lxc-hooks.sh"),
depends_on => { "lxc_installed" },
handle => "lxchookscript";
debian_11|debian_12|debian_13::
'$(lxc_nft_path)/lxc-net-up.nft'
perms => mog('700','root','root'),
copy_from => local_cp('$(lxc_up_nft)'),
depends_on => { 'lxc_installed' },
handle => 'lxc_up_nft_copied';
'$(lxc_nft_path)/lxc-net-down.nft'
perms => mog('700','root','root'),
copy_from => local_cp('$(lxc_down_nft)'),
depends_on => { 'lxc_installed' },
handle => 'lxc_down_nft_copied';
'/etc/systemd/system/lxc-net.service.d/.'
perms => mog('755','root','root'),
create => 'true',
depends_on => { 'lxc_installed' },
handle => 'lxc_net_override_dir';
'/etc/systemd/system/lxc-net.service.d/override.conf'
perms => mog('644','root','root'),
copy_from => local_cp('$(sys.workdir)/inputs/$(def.wmde_libdir)/templates/lxc_host/lxc-net.override.conf'),
depends_on => { 'lxc_installed' },
classes => results('namespace','lxc_net'),
handle => 'lxc_net_override';
"/usr/local/bin/lxc-hook-net"
perms => mog('700','root','root'),
copy_from => local_cp("$(lxc_hook_net)"),
depends_on => { "lxc_installed", "lxchookscript" },
handle => "lxchooknet";
debian|ubuntu::
"$(lxc_net_path)"
perms => mog('644','root', 'root'),
template_data => @(cfg_ip),
template_method => 'mustache',
edit_template => "$(sys.workdir)/inputs/$(def.wmde_libdir)/templates/lxc_host/$(lxc_net_tmpl)",
depends_on => { "lxc_installed" };
"$(lxc_default_path)"
perms => mog('644','root', 'root'),
template_data => @(cfg_ip),
template_method => 'mustache',
edit_template => "$(sys.workdir)/inputs/$(def.wmde_libdir)/templates/lxc_host/$(lxc_default_tmpl)",
depends_on => { "lxc_installed" };
commands:
lxc_net_repaired::
"/usr/bin/systemctl"
args => "daemon-reload",
handle => "lxc_net_reloaded";
services:
lxc_net_repaired::
"lxc-net.service"
service_policy => "restart",
depends_on => { 'lxc_net_reloaded' };
}
body acl lxc_dnsmasq
{
acl_method => "append";
acl_type => "posix";
aces => {
"user:dnsmasq:rx:allow"
};
}
# example lxc.cfg
# cfg data => '
# {
# "name": "proxy",
# "dist": "debian",
# "release": "bookworm",
# "arch": "amd64",
# "bridge": "lxcbr0",
# "ip": "10.0.11.80",
# "group": "",
# "autostart": true,
# "policy": "absent|present",
# "state": "stopped|running|manual",
# }';
# name
# lxc.container.conf:
# # Parameters passed to the template: --dist $(cfg[dist]) --arch $(cfg[arch]) --release $(cfg[release])
bundle agent lxc( cfg )
{
classes:
"cfg_array" expression => strcmp( type( "cfg", "true" ), "data array" );
vars:
cfg_array::
"index" slist => getindices( @(cfg) );
methods:
cfg_array::
"Iterate over config array: $(index)"
usebundle => _lxc( @(cfg[$(index)]) );
!cfg_array::
"Forward config to"
usebundle => _lxc( @(cfg) );
reports:
cfg_array::
"Iterated over cfg array.";
}
# Although all attributes are passed in one data object there are
# qualitative differences. All need the name element.
# lxc-create needs dist, arch and release.
# lxc config file needs autostart and group
# dnsmasq needs the ip
bundle agent _lxc( cfg )
{
classes:
"lxc_exists" expression => returnszero( "/usr/bin/lxc-info $(cfg[name]) 2> /dev/null", "noshell" ), scope => "bundle";
"lxc_host_file_exists" expression => fileexists( "$(lxc_host_file)" ), scope => "bundle";
"lxc_policy_exists" expression => isvariable( "cfg[policy]" );
"lxc_state_exists" expression => isvariable( "cfg[state]" );
"lxc_ports_exists" expression => isvariable( "cfg[ports]" );
"lxc_raw_lines_exists" expression => isvariable( "cfg[raw_lines]" );
lxc_policy_exists::
"lxc_policy_valid" expression => regcmp( "(absent|present)", "$(cfg[policy])" );
lxc_policy_valid::
'present' expression => strcmp( "present", "$(cfg[policy])" );
!lxc_policy_exists::
'present';
lxc_state_exists::
"lxc_state_valid" expression => regcmp( "(stopped|running|manual)", "$(cfg[state])" );
lxc_state_valid::
'running' expression => strcmp( "running", "$(cfg[state])" );
lxc_state_valid::
'manual' expression => strcmp( "manual", "$(cfg[state])" );
!lxc_state_exists::
'running';
lxc_exists::
"lxc_correct_distribution" expression => regline( '^# Parameters passed to the template: --dist $(cfg[dist]) --arch $(cfg[arch]) --release $(cfg[release])$', "$(lxc_dir)/config" ), scope => "bundle";
lxc_ports_exists::
"ports_array" expression => strcmp( type( "cfg[ports]", "false" ), "data" );
vars:
"lxc_host_file" string => "$(lxc_host.lxc_hosts_d)/$(cfg[name])";
"lxc_dir" string => "/var/lib/lxc/$(cfg[name])";
"lxc_rootfs" string => "$(lxc_dir)/rootfs";
'autostart' string => $(cfg[autostart]);
'group' string => $(cfg[group]);
files:
ports_array::
"/var/lib/lxc/$(cfg[name])/ports"
edit_template => "$(sys.workdir)/inputs/$(def.wmde_libdir)/templates/lxc_host/port_forwarding.mustache",
template_data => @(cfg),
template_method => "mustache",
# depends_on => { "lxc_$(cfg[name])_created" },
handle => "$(cfg[name])_ports_created";
!lxc_exists & present::
"/var/lib/lxc/$(cfg[name])/config"
edit_line => lxc_config( "$(autostart)", "$(group)", @(cfg[raw_lines]) ),
depends_on => { "lxc_$(cfg[name])_created" },
handle => "$(cfg[name])_config_created";
methods:
!lxc_exists & present::
"Ensure existence of container ($(cfg[name]))"
usebundle => lxc_create( @(cfg) ),
handle => "lxc_$(cfg[name])_created";
"Ensure static mapping in dnsmasq"
usebundle => lxc_add_static_mapping( @(cfg) ),
depends_on => { "lxc_$(cfg[name])_created" },
handle => "lxc_$(cfg[name])_mapped_statically";
lxc_exists & !present::
"Ensure absence of container ($cfg[name])"
usebundle => lxc_destroy( @(cfg) ),
handle => "lxc_$(cfg[name])_destroyed";
"Ensure absence of static mapping"
usebundle => lxc_remove_static_mapping( @(cfg) ),
depends_on => { "lxc_$(cfg[name])_destroyed" },
handle => "lxc_$(cfg[name])_unmapped_statically";
"Ensure dnsmasq picks up current lxc host configs"
usebundle => reload_dnsmasq,
depends_on => { "lxc_$(cfg[name])_unmapped_statically" },
handle => "reloaded_dnsmasq_for_$(cfg[name])";
present & running::
"Ensure running state of container ($(cfg[name]))"
usebundle => lxc_start( @(cfg[name]) ),
handle => "lxc_$(cfg[name])_started";
present & !(running | manual)::
"Ensure stopped state of container ($(cfg[name]))"
usebundle => lxc_stop( @(cfg[name]) ),
handle => "lxc_$(cfg[name])_stopped";
reports:
"_lxc: raw_lines_$(cfg[name]) [$(lxc_raw_type)] enthält $(cfg[raw_lines])";
"_lxc: raw_lines_arg_$(cfg[name]) [$(lxc_raw_type)] enthält ($-Notation) $(raw_lines_arg_$(cfg[name]))";
lxc_exists & lxc_correct_distribution::
"LX Container $(cfg[name]) already configured, nothing to do";
!lxc_exists & present::
"LXC $(cfg[name]) did not exist and should have been created.";
lxc_exists & !present::
"LXC $(cfg[name]) did exist and should have been destroyed.";
present & running::
"LXC $(cfg[name]) should now be in state RUNNING.";
present & !running::
"LXC $(cfg[name]) should now be in state STOPPED.";
}
bundle edit_line lxc_config( autostart, group, raw )
{
classes:
"autostart_true"
expression => some( $(autostart_lc), true_statements ),
depends_on => { "$(autostart)_lowercased" };
"group_provided"
expression => isgreaterthan( $(group_length), 0 ),
depends_on => { "group_length_measured" };
vars:
"autostart_lc"
string => string_downcase( $(autostart) ),
handle => "$(autostart)_lowercased";
"group_length"
int => string_length( $(group) ),
handle => "group_length_measured";
"true_statements"
slist => { "yes", "true", "on", "1" },
handle => "truth";
insert_lines:
"# This file is managed by CFEngine. Manual changes will be overwritten."
location => first_line,
handle => "warning";
"$(raw)"
depends_on => { "warning" };
autostart_true::
"lxc.start.auto = 1";
!autostart_true::
"lxc.start.auto = 0";
group_provided::
"lxc.group = $(group)";
reports:
"lxc_config 1: autostart ist $(autostart)";
"lxc_config 2: group ist $(group)";
"lxc_config 3: raw enthält $(raw)";
}
body location first_line
{
before_after => "before";
first_last => "first";
select_line_matching => ".*";
}
body location last_line
{
before_after => "after";
first_last => "last";
select_line_matching => ".*";
}
bundle agent lxc_add_static_mapping( cfg )
{
files:
"$(lxc_host.lxc_hosts_d)/$(cfg[name])"
perms => mog( '644', 'root', 'root' ),
content => "$(cfg[name]),$(cfg[ip])",
handle => "mapped_$(cfg[name])";
reports:
"mapped $(cfg[name]) to $(cfg[ip])"
depends_on => { "mapped_$(cfg[name])" };
}
bundle agent lxc_remove_static_mapping( cfg )
{
files:
"$(lxc_host.lxc_hosts_d)/$(cfg[name])"
delete => tidy,
classes => if_repaired(dnsmasq_reload);
reports:
dnsmasq_reload::
"mapped $(cfg[name]) to $(cfg[ip])";
}
bundle agent lxc_add_port_forwarding_rule_config( cfg )
{
}
# When files for static mappings are added dnsmasq automatically loads
# them. But dnsmasq doesn't remove them automatically again when the
# file gets removed.
bundle agent reload_dnsmasq
{
processes:
dnsmasq_reload::
"dnsmasq"
signals => { "hup" };
}
# DOWNLOAD_KEYSERVER="keyserver.ubuntu.com" lxc-create -n manual -t download -- -d debian -a amd64 -r bookworm
bundle agent lxc_create( cfg )
{
classes:
"lxc_dir_btrfs" expression => strcmp( execresult( "/usr/bin/stat -f -c %T /var/lib/lxc" , "noshell", "stdout" ), "btrfs" );
vars:
!lxc_dir_btrfs::
"create_args" slist => {
"-n",
$(cfg[name]),
"-t",
"download",
"--",
"-d",
$(cfg[dist]),
"-a",
$(cfg[arch]),
"-r",
$(cfg[release]),
};
lxc_dir_btrfs::
"create_args" slist => {
"-n",
$(cfg[name]),
"-t",
"download",
"-B",
"btrfs",
"--",
"-d",
$(cfg[dist]),
"-a",
$(cfg[arch]),
"-r",
$(cfg[release]),
};
commands:
"/usr/bin/lxc-create"
arglist => { @(create_args) },
contain => lxc_commands,
handle => "lxc_$(cfg[name])_created";
reports:
"LXC $(cfg[name]) has been created"
depends_on => { "lxc_$(cfg[name])_created" };
}
bundle agent lxc_destroy( cfg )
{
methods:
"Ensure LXC is stopped"
usebundle => lxc_stop( $(cfg[name]) ),
handle => "stopped_$(cfg[name])";
commands:
"/usr/bin/lxc-destroy"
arglist => {
"-n",
$(cfg[name])
},
depends_on => { "stopped_$(cfg[name])" },
handle => "destroyed_$(cfg[name])";
}
bundle agent lxc_start( name )
{
classes:
"lxc_running"
expression => strcmp( execresult( "/usr/bin/lxc-info -n $(name) -s -H", "noshell", "stdout" ), "RUNNING" ),
scope => "bundle";
commands:
!lxc_running::
"/usr/bin/lxc-start"
arglist => {
"-n",
$(name)
},
handle => "lxc_$(name)_started";
reports:
!lxc_running::
"$(name) has been started"
depends_on => { "lxc_$(name)_started" };
}
bundle agent lxc_stop( name )
{
classes:
"lxc_running"
expression => strcmp( execresult( "/usr/bin/lxc-info -n $(name) -s -H", "noshell", "stdout" ), "RUNNING" ),
scope => "bundle";
commands:
lxc_running::
"/usr/bin/lxc-stop"
arglist => {
"-n",
$(name)
},
handle => "lxc_$(name)_stopped";
reports:
lxc_running::
"$(name) has been stopped"
depends_on => { "lxc_$(name)_stopped" };
}
body contain lxc_commands
{
useshell => "noshell";
no_output => "true";
}