Find MAC address

Finding the ethernet address can be hard, but on Linux it is straightforward.

bundle agent test
{
  vars:
    linux::
      "interface" string => execresult("/sbin/ifconfig eth0", "noshell");

    solaris::
      "interface" string => execresult("/usr/sbin/ifconfig bge0", "noshell");

    freebsd::
      "interface" string => execresult("/sbin/ifconfig le0", "noshell");

    darwin::
      "interface" string => execresult("/sbin/ifconfig en0", "noshell");

  classes:
    linux::
      "ok"
        expression => regextract(
          ".*HWaddr ([^\s]+).*(\n.*)*", "$(interface)", "mac"
        );

    solaris::
      "ok"
        expression => regextract(
          ".*ether ([^\s]+).*(\n.*)*", "$(interface)", "mac"
        );

    freebsd::
      "ok"
        expression => regextract(
          ".*ether ([^\s]+).*(\n.*)*", "$(interface)", "mac"
        );

    darwin::
      "ok"
        expression => regextract(
          "(?s).*ether ([^\s]+).*(\n.*)*", "$(interface)", "mac"
        );

  reports:
    ok::
      "MAC address is $(mac[1])";
}

Client-server example

body common control
{
  bundlesequence => { "testbundle" };
  version => "1.2.3";
  #fips_mode => "true";
}
bundle agent testbundle
{
  files:
    "/home/mark/tmp/testcopy"
      comment => "test copy promise",
      copy_from => mycopy("/home/mark/LapTop/words", "127.0.0.1"),
      perms => system,
      depth_search => recurse("inf"),
      classes => satisfied("copy_ok");

    "/home/mark/tmp/testcopy/single_file"
      comment => "test copy promise",
      copy_from => mycopy(
        "/home/mark/LapTop/Cfengine3/trunk/README", "127.0.0.1"
      ),
      perms => system;

  reports:
    copy_ok::
      "Files were copied..";
}
body perms system
{
  mode => "0644";
}
body depth_search recurse(d)
{
  depth => "$(d)";
}
body copy_from mycopy(from, server)
{
  source => "$(from)";
  servers => { "$(server)" };
  compare => "digest";
  encrypt => "true";
  verify => "true";
  copy_backup => "true";
  #/false/timestamp
  purge => "false";
  type_check => "true";
  force_ipv4 => "true";
  trustkey => "true";
}
body classes satisfied(x)
{
  promise_repaired => { "$(x)" };
  persist_time => "0";
}
body server control
{
  allowconnects => { "127.0.0.1", "::1" };
  allowallconnects => { "127.0.0.1", "::1" };
  trustkeysfrom => { "127.0.0.1", "::1" };
  # allowusers
}
bundle server my_access_rules()
{
  access:
    "/home/mark/LapTop" admit => { "127.0.0.1" };
}

Read from a TCP socket

body common control
{
  bundlesequence => { "example" };
}
bundle agent example
{
  vars:
    "my80"
      string => readtcp(
        "research.iu.hio.no",
        "80",
        "GET /index.php HTTP/1.1$(const.r)$(const.n)Host: research.iu.hio.no$(const.r)$(const.n)$(const.r)$(const.n)",
        20
      );

  classes:
    "server_ok" expression => regcmp(".*200 OK.*\n.*", "$(my80)");

  reports:
    server_ok::
      "Server is alive";

    !server_ok::
      "Server is not responding - got $(my80)";
}

Set up a PXE boot server

Use CFEngine to set up a PXE boot server.

body common control
{
  bundlesequence => { "pxe" };
  inputs => { "$(sys.libdir)/stdlib.cf" };
}
bundle agent pxe
{
  vars:
    "software" slist => { "atftp", "dhcp-server", "syslinux", "apache2" };

    "dirs"
      slist => {
        "/tftpboot",
        "/tftpboot/CFEngine/rpm",
        "/tftpboot/CFEngine/inputs",
        "/tftpboot/pxelinux.cfg",
        "/tftpboot/kickstart",
        "/srv/www/repos",
      };

    "tmp_location" string => "/tftpboot/CFEngine/inputs";

    # Distros that we can install
    "rh_distros" slist => { "4.7", "5.2" };
    "centos_distros" slist => { "5.2" };

    # File contents of atftp configuration
    "atftpd_conf"
      string => "

ATFTPD_OPTIONS=\"--daemon \"
ATFTPD_USE_INETD=\"no\"
ATFTPD_DIRECTORY=\"/tftpboot\"
ATFTPD_BIND_ADDRESSES=\"\"
       ";

    # File contents of DHCP configuration
    "dhcpd"
      string => "

DHCPD_INTERFACE=\"eth0\"
DHCPD_RUN_CHROOTED=\"yes\"
DHCPD_CONF_INCLUDE_FILES=\"\"
DHCPD_RUN_AS=\"dhcpd\"
DHCPD_OTHER_ARGS=\"\"
DHCPD_BINARY=\"\"
       ";

    "dhcpd_conf"
      string => "

allow booting;
allow bootp;
ddns-update-style none; ddns-updates off;
 subnet 192.168.0.0 netmask 255.255.255.0 {
   range 192.168.0.20 192.168.0.254;
   default-lease-time 3600;
   max-lease-time 4800;
   option routers 192.168.0.1;
   option domain-name \"test.CFEngine.com\";
   option domain-name-servers 192.168.0.1;
   next-server 192.168.0.1;
   filename \"pxelinux.0\";
 }
 group {
   host node1 {
     # Dummy machine

     hardware ethernet 00:0F:1F:94:FE:07;
     fixed-address 192.168.0.11;
     option host-name \"node1\";
   }
   host node2 {
     # Dell Inspiron 1150

     hardware ethernet 00:0F:1F:0E:70:E7;
     fixed-address 192.168.0.12;
     option host-name \"node2\";
   }
 }
        ";

    # File contains of Apache2 HTTP configuration
    "httpd_conf"
      string => "

<Directory /srv/www/repos>
Options Indexes
AllowOverride None
</Directory>
Alias /repos /srv/www/repos

<Directory /tftpboot/distro/RHEL/5.2>
Options Indexes
AllowOverride None
</Directory>
Alias /distro/rhel/5.2 /tftpboot/distro/RHEL/5.2
<Directory /tftpboot/distro/RHEL/4.7>
Options Indexes
AllowOverride None
</Directory>
Alias /distro/rhel/4.7 /tftpboot/distro/RHEL/4.7
<Directory /tftpboot/distro/CentOS/5.2>
Options Indexes
AllowOverride None
</Directory>
Alias /distro/centos/5.2 /tftpboot/distro/CentOS/5.2
<Directory /tftpboot/kickstart>
Options Indexes
AllowOverride None
</Directory>
Alias /kickstart /tftpboot/kickstart
<Directory /tftpboot/CFEngine>
Options Indexes
AllowOverride None
</Directory>
Alias /CFEngine /tftpboot/CFEngine
        ";

    # File contains of Kickstart for RHEL5 configuration
    "kickstart_rhel5_conf"
      string => "


auth  --useshadow  --enablemd5
bootloader --location=mbr
clearpart --all --initlabel
graphical
firewall --disabled
firstboot --disable
key 77244a6377a8044a
keyboard no
lang en_US
logging --level=info
url --url=http://192.168.0.1/distro/rhel/5.2
network --bootproto=dhcp --device=eth0 --onboot=on
reboot
rootpw --iscrypted $1$eOnXdDPF$279sQ//zry6rnQktkATeM0
selinux --disabled
timezone --isUtc Europe/Oslo
install
part swap --bytes-per-inode=4096 --fstype=\"swap\" --recommended
part / --bytes-per-inode=4096 --fstype=\"ext3\" --grow --size=1
%packages
@core
@base
db4-devel
openssl-devel
gcc
flex
bison
libacl-devel
libselinux-devel
pcre-devel

device-mapper-multipath
-sysreport
%post
cd /root
rpm -i http://192.168.0.1/CFEngine/rpm/CFEngine-3.0.1b1-1.el5.i386.rpm

cd /etc/yum.repos.d
wget http://192.168.0.1/repos/RHEL5.Base.repo
rpm --import /etc/pki/rpm-gpg/*
yum clean all
yum update
mkdir -p /root/CFEngine_init
cd /root/CFEngine_init
wget -nd -r http://192.168.0.1/CFEngine/inputs/
/usr/local/sbin/cf-agent -B
/usr/local/sbin/cf-agent
        ";

    # File contains of PXElinux boot menu
    "pxelinux_boot_menu"
      string => "

boot options:
     rhel5   - install 32 bit i386 RHEL 5.2             (MANUAL)
     rhel5w  - install 32 bit i386 RHEL 5.2             (AUTO)
     rhel4   - install 32 bit i386 RHEL 4.7 AS          (MANUAL)
     centos5 - install 32 bit i386 CentOS 5.2 (Desktop) (MANUAL)
        ";

    # File contains of PXElinux default configuration
    "pxelinux_default"
      string => "


default rhel5
timeout 300
prompt 1
display pxelinux.cfg/boot.msg
F1 pxelinux.cfg/boot.msg

label rhel5
   kernel vmlinuz-RHEL5U2
   append initrd=initrd-RHEL5U2 load_ramdisk=1 ramdisk_size=16384 install=http://192.168.0.1/distro/rhel/5.2

label rhel5w
   kernel vmlinuz-RHEL5U2
   append initrd=initrd-RHEL5U2 load_ramdisk=1 ramdisk_size=16384 ks=http://192.168.0.1/kickstart/kickstart-RHEL5U2.cfg

label rhel4
   kernel vmlinuz-RHEL4U7
   append initrd=initrd-RHEL4U7 load_ramdisk=1 ramdisk_size=16384 install=http://192.168.0.1/distro/rhel/4.7

label centos5
   kernel vmlinuz-CentOS5.2
   append initrd=initrd-CentOS5.2 load_ramdisk=1 ramdisk_size=16384 install=http://192.168.0.1/distro/centos/5.2
        ";

    # File contains of specified PXElinux default to be a RHEL5 webserver
    "pxelinux_rhel5_webserver"
      string => "


default rhel5w
label rhel5w
   kernel vmlinuz-RHEL5U2
   append initrd=initrd-RHEL5U2 load_ramdisk=1 ramdisk_size=16384 ks=http://192.168.0.1/kickstart/kickstart-RHEL5U2.cfg
        ";

    # File contains of a local repository for RHEL5
    "rhel5_base_repo"
      string => "


[Server]
name=Server
baseurl=http://192.168.0.1/repos/rhel5/Server/
enable=1
[VT]
name=VT
baseurl=http://192.168.0.1/repos/rhel5/VT/
enable=1
[Cluster]
name=Cluster
baseurl=http://192.168.0.1/repos/rhel5/Cluster/
enable=1
[ClusterStorage]
name=Cluster Storage
baseurl=http://192.168.0.1/repos/rhel5/ClusterStorage/
enable=1
        ";
  #####################################################
  files:
    packages_ok::
      # Create files/dirs and edit the new files
      "/tftpboot/distro/RHEL/$(rh_distros)/." create => "true";
      "/tftpboot/distro/CentOS/$(centos_distros)/." create => "true";
      "$(dirs)/." create => "true";

      "/tftpboot/pxelinux.cfg/boot.msg"
        create => "true",
        perms => mo("644", "root"),
        edit_line => append_if_no_line("$(pxelinux_boot_menu)"),
        edit_defaults => empty;

      "/tftpboot/pxelinux.cfg/default"
        create => "true",
        perms => mo("644", "root"),
        edit_line => append_if_no_line("$(pxelinux_default)"),
        edit_defaults => empty;

      "/tftpboot/pxelinux.cfg/default.RHEL5.webserver"
        create => "true",
        perms => mo("644", "root"),
        edit_line => append_if_no_line("$(pxelinux_rhel5_webserver)"),
        edit_defaults => empty;

      "/tftpboot/kickstart/kickstart-RHEL5U2.cfg"
        create => "true",
        perms => mo("644", "root"),
        edit_line => append_if_no_line("$(kickstart_rhel5_conf)"),
        edit_defaults => empty;

      "/srv/www/repos/RHEL5.Base.repo"
        create => "true",
        perms => mo("644", "root"),
        edit_line => append_if_no_line("$(rhel5_base_repo)"),
        edit_defaults => empty;

      # Copy files
      "/tftpboot"
        copy_from => local_cp("/usr/share/syslinux"),
        depth_search => recurse("inf"),
        file_select => pxelinux_files,
        action => immediate;

      "$(tmp_location)"
        perms => m("644"),
        copy_from => local_cp("/var/cfengine/inputs"),
        depth_search => recurse("inf"),
        file_select => input_files,
        action => immediate;

      # Edit atftp, dhcp and apache2 configurations
      "/etc/sysconfig/atftpd"
        edit_line => append_if_no_line("$(atftpd_conf)"),
        edit_defaults => empty,
        classes => satisfied("atftpd_ready");

      "/etc/sysconfig/dhcpd"
        edit_line => append_if_no_line("$(dhcpd)"),
        edit_defaults => empty;

      "/etc/dhcpd.conf"
        edit_line => append_if_no_line("$(dhcpd_conf)"),
        edit_defaults => empty,
        classes => satisfied("dhcpd_ready");

      "/etc/apache2/httpd.conf"
        edit_line => append_if_no_line("$(httpd_conf)"),
        edit_defaults => std_defs,
        classes => satisfied("apache2_ok");

      # Make a static link
      "/tftpboot/pxelinux.cfg/C0A8000C"
        link_from => mylink("/tftpboot/pxelinux.cfg/default.RHEL5.webserver");

    # Hash comment some lines for apaches
    apache2_ok::
      "/etc/apache2/httpd.conf"
        edit_line => comment_lines_matching_apache2("#"),
        classes => satisfied("apache2_ready");

  commands:
    # Restart services
    atftpd_ready::
      "/etc/init.d/atftpd restart";

    dhcpd_ready::
      "/etc/init.d/dhcpd restart";

    apache2_ready::
      "/etc/init.d/apache2 restart";
  #####################################################
  packages:
    ipv4_192_168_0_1::
      # Only the PXE boot server
      "$(software)"
        package_policy => "add",
        package_method => zypper,
        classes => satisfied("packages_ok");
}
body file_select pxelinux_files
{
  leaf_name => { "pxelinux.0" };
  file_result => "leaf_name";
}
body copy_from mycopy_local(from, server)
{
  source => "$(from)";
  compare => "digest";
}
body link_from mylink(x)
{
  source => "$(x)";
  link_type => "symlink";
}
body classes satisfied(new_class)
{
  promise_kept => { "$(new_class)" };
  promise_repaired => { "$(new_class)" };
}
bundle edit_line comment_lines_matching_apache2(comment)
{
  vars:
    "regex"
      slist => {
        "\s.*Options\sNone", "\s.*AllowOverride\sNone", "\s.*Deny\sfrom\sall"
      };

  replace_patterns:
    "^($(regex))$" replace_with => comment("$(comment)");
}
body file_select input_files
{
  leaf_name => { ".*.cf", ".*.dat", ".*.txt" };
  file_result => "leaf_name";
}

Resolver management

bundle common g
{
  vars:
    "searchlist" slist => { "search iu.hio.no", "search cfengine.com" };
    "nameservers" slist => { "128.39.89.10", "128.39.74.16", "192.168.1.103" };

  classes:
    "am_name_server"
      expression => reglist("@(nameservers)", "$(sys.ipv4[eth1])");
}
body common control
{
  any::
    bundlesequence => { "g", resolver(@(g.searchlist), @(g.nameservers)) };
    domain => "iu.hio.no";
}
bundle agent resolver(s, n)
{
  files:
    # When passing parameters down, we have to refer to
    # a source context
    "$(sys.resolv)"
      # test on "/tmp/resolv.conf" #
      create => "true",
      edit_line => doresolv("@(this.s)", "@(this.n)"),
      edit_defaults => reconstruct;
  # or edit_defaults => modify
}
bundle edit_line doresolv(s, n)
{
  vars:
    "line" slist => { @(s), @(n) };

  insert_lines:
    "$(line)";
}
body edit_defaults reconstruct
{
  empty_file_before_editing => "true";
  edit_backup => "false";
  max_file_size => "100000";
}
body edit_defaults modify
{
  empty_file_before_editing => "false";
  edit_backup => "false";
  max_file_size => "100000";
}

Mount NFS filesystem

body common control
{
  bundlesequence => { "mounts" };
}

bundle agent mounts
{
  storage:
    "/mnt" mount => nfs("slogans.iu.hio.no", "/home");
}
body mount nfs(server, source)
{
  mount_type => "nfs";
  mount_source => "$(source)";
  mount_server => "$(server)";
  #mount_options => { "rw" };
  edit_fstab => "true";
  unmount => "true";
}

Unmount NFS filesystem

body common control
{
  bundlesequence => { "mounts" };
}
bundle agent mounts
{
  storage:
    # Assumes the filesystem has been exported
    "/mnt" mount => nfs("server.example.org", "/home");
}
body mount nfs(server, source)
{
  mount_type => "nfs";
  mount_source => "$(source)";
  mount_server => "$(server)";
  edit_fstab => "true";
  unmount => "true";
}