Wednesday, October 25, 2017

ansible debugging new tasks

When adding new tasks to ansible playbook it's handy do bind them between the following other two tasks to make debugging simpler.


- name: Print variable I am using
  debug:
    msg: "my variable: {{ foo }}"

# ... whatever i'm adding ... which does something to {{ foo }}

- fail:
    msg: "Stopping so I can easily re-run"

Saturday, July 2, 2016

Fedora 24 Gnome Shell Window Size

To make the default title bar smaller in Gnome 3.20 on Fedora 24 create the following file and restart with "killall -3 gnome-shell".
$ cat ~/.config/gtk-3.0/gtk.css 
/* shrink headebars */
headerbar {
    min-height: 24px;
    padding-left: 2px; /* same as childrens vertical margins for nicer proportions */
    padding-right: 2px;
}

headerbar entry,
headerbar spinbutton,
headerbar button,
headerbar separator {
    margin-top: 2px; /* same as headerbar side padding for nicer proportions */
    margin-bottom: 2px;
}

/* shrink ssd titlebars */
.default-decoration {
    min-height: 0; /* let the entry and button drive the titlebar size */ 
    padding: 2px
}

.default-decoration .titlebutton {
    min-height: 16px; /* tweak these two props to reduce button size */
    min-width: 16px;
}
$ 

Thursday, June 16, 2016

Fix all the permissions uid/gid

To reset UID/GID on all files:

# for i in $(rpm -qa); do rpm --setugids $i; done

To reset permissions on all files:

# for i in $(rpm -qa); do rpm --setperms $i; done

Wednesday, June 1, 2016

gnome-shell super maximize

Ctrl-Alt-Enter

  • This is a two way toggle. Type it once to "super maximize". Type it again to un-"super maximize".
  • In gnome-shell this will maximize the currently focused window to the point that its titlebar will not be seen.
  • It will only do this to the Terminal (including xterm).
  • I've hit this by accident a few times but didn't know how to undo it, so I'm logging it here should I forget.
  • It's not listed on Gnome.org Keyboard Shortcuts or my first 5 Google results.

Monday, May 9, 2016

synergy dekstop sharing

I bought access to Synergy from the company supporting it because I've been using it for years and they deserve a few bucks. I use EPEL but don't want it clobber the version that I download from synergy-project.org. Thus, I have the following in my epel.repo:

$ sudo head /etc/yum.repos.d/epel.repo
[epel]
name=Extra Packages for Enterprise Linux 7 - $basearch
#baseurl=http://download.fedoraproject.org/pub/epel/7/$basearch
mirrorlist=https://mirrors.fedoraproject.org/metalink?repo=epel-7&arch=$basearch
failovermethod=priority
enabled=1
gpgcheck=1
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-EPEL-7
exclude=synergy*

$
This is another one of those posts I do to remind myself about what I did.

Friday, July 3, 2015

Burn RHEL7.1 ISO from RHEL7.1

I ran the following on a RHEL7.1 system to make a RHEL7.1 bootable ISO.

[root@box iso]# ls -lh rhel-server-7.1-x86_64-dvd.iso 
-rw-rw-r--. 1 qemu qemu 3.7G Mar 17 11:09 rhel-server-7.1-x86_64-dvd.iso
[root@box iso]# 

[root@box iso]# growisofs -dvd-compat -Z /dev/cdrom=rhel-server-7.1-x86_64-dvd.iso 
Executing 'builtin_dd if=rhel-server-7.1-x86_64-dvd.iso of=/dev/cdrom obs=32k seek=0'
/dev/cdrom: "Current Write Speed" is 8.2x1352KBps.
   15204352/3890216960 ( 0.4%) @3.1x, remaining 21:14 RBU 100.0% UBU  10.0%
   ...
   3864657920/3890216960 (99.3%) @7.3x, remaining 0:03 RBU 100.0% UBU 100.0%
builtin_dd: 1899520*2KB out @ average 5.4x1352KBps
/dev/cdrom: flushing cache
/dev/cdrom: updating RMA
/dev/cdrom: closing disc
/dev/cdrom: reloading tray
[root@box iso]#

I think growisofs has a slightly confusing name and an odd syntax.

Wednesday, March 18, 2015

Unpack a Python object with vars() to see what is inside of it

#!/usr/bin/env python
# Filename:                unpack.py
# Description:             This code will unpack a python object
# Supported Langauge(s):   Python 2.7.x
# Time-stamp:              <2015-03-18 18:41:56 none> 
# -------------------------------------------------------
# I had to figure this out twice I am logging it this time. 
# -------------------------------------------------------

# Suppose we have some object...

class FirstClass:
    def setdata(self, value):
            self.data = value
    def display(self):
            print self.data

# ...and someone instantiated it

x = FirstClass()
y = FirstClass()
x.setdata("King Arthur")
y.setdata(3.14159)

# If someone hands it to me and I want to know what inside 
# it, then printing it just gives me something like 
# <__main__.FirstClass instance at 0x7f794fab7b00>

print x

# Since I have the code to the class I can just use the
# display method. 

x.display()

# But what if I don't know about the dispaly method?
# Is there some way I an unpack any arbitrary object?

vars(x)

# The above returns {'data': 'King Arthur'} . 
# For a more complicated object you can pprint it. 

from pprint import pprint
pprint (vars(x))

Try dir([object]) too.

Thursday, January 1, 2015

HA Web Server via Pacemaker on three VMs using an iSCSI VM

A number of years ago I configured a four-node RHEL6 cluster using cman,
rgmanager, clvm, gfs2, a SAN and Dell's CMC fencing to support live
migration and automatic failover of Xen/KVM virtual machines.
I ended up trying prototype this using VMWare Fusion on a Mac but was
unable to because there were not any fencing options for the VM
platform at the time.

Today I was glad to set up a three-node RHEL7 HA web cluster
all within my laptop using only four KVM VMs using pacemaker,
corosync, and a VM running an iSCSI server. I used the following
documents for reference.



Below are some of my notes, mainly for my own purposes.


Get the software

subscription-manager register
subscription-manager attach --pool=$POOL
subscription-manager repos --disable=*
subscription-manager repos --enable=rhel-7-server-rpms
subscription-manager repos --enable=rhel-ha-for-rhel-7-server-rpms
yum -y install pacemaker corosync fence-agents-all fence-agents-virsh fence-virt pcs  dlm lvm2-cluster gfs2-utils  

Use consistent /etc/hosts

127.0.0.1   localhost localhost.localdomain localhost4 localhost4.localdomain4
::1         localhost localhost.localdomain localhost6 localhost6.localdomain6
192.168.122.87  pcs-a pcs-a.example.com
192.168.122.26  pcs-b pcs-b.example.com
192.168.122.129 pcs-c pcs-c.example.com
192.168.122.140 iscsi iscsi.example.com
192.168.122.141 web web.example.com
Note that I cloned my VM (pcs-a) at this point into pcs-{b,c} at this point.


Start the cluster

Note that this was applied to the other nodes.
passwd hacluster 
pcs cluster auth pcs-a.example.com pcs-b.example.com pcs-c.example.com   
Notice that I didn't need to set up fencing just to get them talking to eachother.
[root@pcs-a ~]# pcs cluster setup --name rh7nodes pcs-a.example.com pcs-b.example.com pcs-c.example.com   
Shutting down pacemaker/corosync services...
Redirecting to /bin/systemctl stop  pacemaker.service
Redirecting to /bin/systemctl stop  corosync.service
Killing any remaining services...
Removing all cluster configuration files...
pcs-a.example.com: Succeeded
pcs-b.example.com: Succeeded
pcs-c.example.com: Succeeded
[root@pcs-a ~]# pcs cluster start --all  

Get Fencing Working

First you need to configure your hypervisor (my laptop) to fence the VMs using fence-virtd as described in
How can I set up virtual fencing on my KVM Virtual machine cluster?


Once that's working (using the multicast option), create the fence device:
pcs stonith describe fence_xvm  
pcs stonith create virtfence_xvm fence_xvm  
pcs stonith update virtfence_xvm pcmk_host_map="pcs-a.example.com:pcs-a;pcs-b.example.com:pcs-b;pcs-c.example.com:pcs-c" key_file=/etc/corosync/fence_xvm.key  
After following the above I was unable at first to fence a node.
[root@pcs-a ~]# stonith_admin --reboot pcs-c.example.com
Command failed: No route to host
[root@pcs-a ~]# 
I found that my host hadn't joined the multicast group which I
addressed by restarting fenced and my VMs.

ss -l | grep 225.0.0.12
tcpdump was very helpful in pointing out that I needed to open the
firewall:

tcpdump -i virbr0 -n port 1229 
iptables -I INPUT -p udp --dport 1229 -j ACCEPT
To let fence_virtd service get accessed.
# lsof -i UDP:1229
COMMAND     PID USER   FD   TYPE DEVICE SIZE/OFF NODE NAME
fence_vir 11781 root    8u  IPv4 506550      0t0  UDP *:zented 
# ps axu | grep 11781
root     10197  0.0  0.0 112640   960 pts/5    S+   16:02   0:00 grep --color=auto 11781
root     11781  0.0  0.0 158832  4868 ?        Ss   11:23   0:00 /usr/sbin/fence_virtd -w
# 
The other issue I ran into is that you must pass the fence_xvm command
NOT the hostname but the name that virsh uses; i.e. this will not
work:

[root@pcs-a ~]# fence_xvm -H pcs-c.example.com -dddd | tail -5
Issuing TCP challenge
Responding to TCP challenge
TCP Exchange + Authentication done... 
Waiting for return value from XVM host
Operation failed
[root@pcs-a ~]# 
Because `virsh list | grep pcs-c.example.com` will not return
anything. Instead, the following worked because I had named by VM
rhel7-pcs-c using virsh.
fence_xvm -H rhel7-pcs-c -dddddd
Finally, when debugging on the host, stop the fence_virtd daemon via
the service controller and watch the error messages directly:

service fence_virtd stop
 /usr/sbin/fence_virtd -d999 -F

Configure the iSCSI server

I made a new VM called iscsi.example.com configured it as an iSCSI server by following the RHE7 Storage Admin Guide. I then created a file based block device to serve as a LUN.

[root@iscsi ~]# systemctl enable target
ln -s '/usr/lib/systemd/system/target.service' '/etc/systemd/system/multi-user.target.wants/target.service'
[root@iscsi ~]# targetcli
Warning: Could not load preferences file /root/.targetcli/prefs.bin.
targetcli shell version 2.1.fb34
Copyright 2011-2013 by Datera, Inc and others.
For help on commands, type 'help'.

/> ls
o- / ......................................................................................................................... [...]
  o- backstores .............................................................................................................. [...]
  | o- block .................................................................................................. [Storage Objects: 0]
  | o- fileio ................................................................................................. [Storage Objects: 0]
  | o- pscsi .................................................................................................. [Storage Objects: 0]
  | o- ramdisk ................................................................................................ [Storage Objects: 0]
  o- iscsi ............................................................................................................ [Targets: 0]
  o- loopback ......................................................................................................... [Targets: 0]
/> iscsi/
/iscsi> create
Created target iqn.2003-01.org.linux-iscsi.iscsi.x8664:sn.9e8fba51d7f4.
Created TPG 1.
/iscsi> ls
o- iscsi .............................................................................................................. [Targets: 1]
  o- iqn.2003-01.org.linux-iscsi.iscsi.x8664:sn.9e8fba51d7f4 ............................................................. [TPGs: 1]
    o- tpg1 ................................................................................................. [no-gen-acls, no-auth]
      o- acls ............................................................................................................ [ACLs: 0]
      o- luns ............................................................................................................ [LUNs: 0]
      o- portals ...................................................................................................... [Portals: 0]
/iscsi>

/backstores> /backstores/fileio create www /var/fileio/www 5G write_back=false
Created fileio www with size 5368709120
/backstores> ls
o- backstores ..................................................................
  o- block .....................................................................
  o- fileio ....................................................................
  | o- www .....................................................................
  o- pscsi .....................................................................
  o- ramdisk ...................................................................
/backstores>
/backstores> /iscsi
/iscsi> ls
o- iscsi .......................................................................
  o- iqn.2003-01.org.linux-iscsi.iscsi.x8664:sn.9e8fba51d7f4 ...................
    o- tpg1 ....................................................................
      o- acls ..................................................................
      o- luns ..................................................................
      o- portals ...............................................................
/iscsi> 
/iscsi> iqn.2003-01.org.linux-iscsi.iscsi.x8664:sn.9e8fba51d7f4/tpg1/
/iscsi/iqn.20...ba51d7f4/tpg1> portals/ create 192.168.122.140 
Using default IP port 3260
Created network portal 192.168.122.140:3260.
/iscsi/iqn.20...ba51d7f4/tpg1> ls
o- tpg1 ................................................. [no-gen-acls, no-auth]
  o- acls ............................................................ [ACLs: 0]
  o- luns ............................................................ [LUNs: 0]
  o- portals ...................................................... [Portals: 1]
    o- 192.168.122.140:3260 ............................................... [OK]
/iscsi/iqn.20...ba51d7f4/tpg1> luns/ create /backstores/fileio/www 

Created LUN 0.
/iscsi/iqn.20...ba51d7f4/tpg1> ls
o- tpg1 ................................................. [no-gen-acls, no-auth]
  o- acls ............................................................ [ACLs: 0]
  o- luns ............................................................ [LUNs: 1]
  | o- lun0 ..................................... [fileio/www (/var/fileio/www)]
  o- portals ...................................................... [Portals: 1]
    o- 192.168.122.140:3260 ............................................... [OK]
/iscsi/iqn.20...ba51d7f4/tpg1>

On pcs-{a,b,c} I ran `yum install iscsi-initiator-utils` as per a Red Hat knowledge base article and identified the generated InitiatorName:


[root@pcs-c ~]# cat /etc/iscsi/initiatorname.iscsi
InitiatorName=iqn.1994-05.com.redhat:44e8828a7868
[root@pcs-c ~]# 
From there I assigned the ACLs for my three hosts:

/iscsi/iqn.20...7f4/tpg1/acls> create iqn.1994-05.com.redhat:553420881b94
Created Node ACL for iqn.1994-05.com.redhat:553420881b94
Created mapped LUN 0.
/iscsi/iqn.20...7f4/tpg1/acls> create iqn.1994-05.com.redhat:44e8828a7868
Created Node ACL for iqn.1994-05.com.redhat:44e8828a7868
Created mapped LUN 0.
/iscsi/iqn.20...7f4/tpg1/acls> create iqn.1994-05.com.redhat:d7163d296480
Created Node ACL for iqn.1994-05.com.redhat:d7163d296480
Created mapped LUN 0.
/iscsi/iqn.20...7f4/tpg1/acls> ls
o- acls ..................................................................................... [ACLs: 3]
  o- iqn.1994-05.com.redhat:44e8828a7868 ............................................. [Mapped LUNs: 1]
  | o- mapped_lun0 ............................................................. [lun0 fileio/www (rw)]
  o- iqn.1994-05.com.redhat:553420881b94 ............................................. [Mapped LUNs: 1]
  | o- mapped_lun0 ............................................................. [lun0 fileio/www (rw)]
  o- iqn.1994-05.com.redhat:d7163d296480 ............................................. [Mapped LUNs: 1]
    o- mapped_lun0 ............................................................. [lun0 fileio/www (rw)]
/iscsi/iqn.20...7f4/tpg1/acls> 
I then had them all connect to the same block device:

[root@pcs-c ~]# fdisk -l /dev/sda 
fdisk: cannot open /dev/sda: No such file or directory
[root@pcs-c ~]# iscsiadm -m node -T iqn.2003-01.org.linux-iscsi.iscsi.x8664:sn.9e8fba51d7f4 -p iscsi.example.com -l
Logging in to [iface: default, target: iqn.2003-01.org.linux-iscsi.iscsi.x8664:sn.9e8fba51d7f4, portal: 192.168.122.140,3260] (multiple)
Login to [iface: default, target: iqn.2003-01.org.linux-iscsi.iscsi.x8664:sn.9e8fba51d7f4, portal: 192.168.122.140,3260] successful.
[root@pcs-c ~]# fdisk -l /dev/sda 

Disk /dev/sda: 5368 MB, 5368709120 bytes, 10485760 sectors
Units = sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 4194304 bytes

[root@pcs-c ~]# 


Create an Exclusive LV


I then followed the second half of the documentation to create a file system and LV for the web tree.

pvcreate /dev/sda1
vgcreate my_vg /dev/sda1
lvcreate -L1000 -n my_lv my_vg 
mkfs.ext4 /dev/my_vg/my_lv
mount /dev/my_vg/my_lv /var/www/
mkdir /var/www/html
mkdir /var/www/cgi-bin
mkdir /var/www/error
restorecon -R /var/www
echo "hello" > /var/www/html/index.html
umount /var/www

One thing I like about the example is that I didn't need to use CLVM
since I don't have a true shared file system. I only needed to tell
the cluster that access to a vertain LV was to be managed by it and
for it to be accessed by only one node at a time. One safegard against
the filesystem corruption (in addition to fencing) was to tell LVM to
not configure my iSCSI LV as an auto_activation_volume by editing the
volume_list to include only the local LVs in /etc/lvm/lvm.conf.


[root@pcs-a ~]# lvs
  LV    VG    Attr       LSize    Pool Origin Data%  Move Log Cpy%Sync Convert
  my_lv my_vg -wi-a----- 1000.00m                                             
  root  rhel  -wi-ao----   14.81g                                             
  swap  rhel  -wi-ao----    1.70g                                             
[root@pcs-a ~]# 

[root@pcs-b ~]# lvs
  LV   VG   Attr       LSize  Pool Origin Data%  Move Log Cpy%Sync Convert
  root rhel -wi-ao---- 14.81g                                             
  swap rhel -wi-ao----  1.70g                                             
[root@pcs-b ~]# 

[root@pcs-c ~]# lvs
  LV   VG   Attr       LSize  Pool Origin Data%  Move Log Cpy%Sync Convert
  root rhel -wi-ao---- 14.81g                                             
  swap rhel -wi-ao----  1.70g                                             
[root@pcs-c ~]# 

Configure the Cluster Resources

I continued with the the documentation and created by cluster resources starting with the storage resources.

[root@pcs-a ~]# pcs resource create my_lvm LVM volgrpname=my_vg exclusive=true --group apachegroup 

[root@pcs-a ~]# pcs resource show
 Resource Group: apachegroup
     my_lvm     (ocf::heartbeat:LVM):   Started 
[root@pcs-a ~]# pcs resource create my_fs Filesystem device="/dev/my_vg/my_lv" directory="/var/www" fstype="ext4" --group apachegroup 
[root@pcs-a ~]# 
Earlier I had decided that 192.168.122.141 would be a shared IP
address for the host web.example.com. I also configured a new virtual
NIC (which you can do without rebooting a KVM host) for each node and
configured that NIC to not be started on boot, since the cluster will
manage it; e.g. a similar file as below exists on the other nodes but
varies by MAC.

[root@pcs-c ~]# cat /etc/sysconfig/network-scripts/ifcfg-ens8 
HWADDR="52:54:00:c8:71:77"
TYPE="Ethernet"
BOOTPROTO="none"
DEFROUTE="yes"
NAME="ens8"
ONBOOT="no"
[root@pcs-c ~]# 
I then configured this NIC to hold my floating IP address and also
configured the apache resource group.

[root@pcs-a ~]# pcs resource create VirtualIP IPaddr2 ip=192.168.122.141 nic=ens8 cidr_netmask=24 --group apachegroup 
[root@pcs-a ~]# pcs resource create Website apache configfile="/etc/httpd/conf/httpd.conf" statusurl="http://127.0.0.1/server-status" --group apachegroup 
[root@pcs-a ~]# 

I ran into some issues with getting the VirtualIP and Website
resources to start as per `pcs status` and spent time chasing
after an arp issue using `ip n` on the nodes and `tcpdump -i virbr0`
on the hypervisor before I realized I had assigned an address from a
different vlan by accident. This was a good experience however
because I became acquainted with the following.

[root@pcs-a ~]# pcs resource show
 Resource Group: apachegroup
     my_lvm     (ocf::heartbeat:LVM):   Started pcs-b.example.com 
     my_fs      (ocf::heartbeat:Filesystem):    Started pcs-b.example.com 
     VirtualIP  (ocf::heartbeat:IPaddr2):       Stopped 
     Website    (ocf::heartbeat:apache):        Stopped 
[root@pcs-a ~]# pcs resource show

[root@pcs-a ~]# pcs resource debug-start VirtualIP
Operation start for VirtualIP (ocf:heartbeat:IPaddr2) returned 1
 >  stderr: ERROR: Unable to find nic or netmask.
 >  stderr: ERROR: [findif] failed
[root@pcs-a ~]# 

[root@pcs-a pcsd]# pcs resource show VirtualIP
 Resource: VirtualIP (class=ocf provider=heartbeat type=IPaddr2)
  Attributes: ip=198.168.122.141 cidr_netmask=24 
  Operations: start interval=0s timeout=20s (VirtualIP-start-timeout-20s)
              stop interval=0s timeout=20s (VirtualIP-stop-timeout-20s)
              monitor interval=10s timeout=20s (VirtualIP-monitor-interval-10s)
[root@pcs-a pcsd]# 

[root@pcs-a pcsd]# /usr/libexec/heartbeat/send_arp -A -i 200 -r 5 -p /var/run/resource-agents/send_arp-198.168.122.141 ens8 198.168.122.141  auto not_used not_used
ARPING 198.168.122.141 from 198.168.122.141 ens8
Sent 5 probes (5 broadcast(s))
Received 0 response(s)
[root@pcs-a pcsd]# 
Can you imagine copying/pasting the above without realizing that I was
not using 192.168.... but 198.168.... ? Often something
simple. Eventually I noticed it while watching the ARPs on my virtual
bridge:

# tcpdump -i virbr0 | grep -i arp
14:04:17.183244 ARP, Request who-has web tell pcs-a, length 28
14:04:17.363589 ARP, Request who-has pcs-b tell pcs-c, length 28
14:04:17.363684 ARP, Reply pcs-b is-at 52:54:00:1c:c4:1c (oui Unknown), length 28
14:04:17.779637 ARP, Request who-has laptop.example.com tell pcs-b, length 28
14:04:17.779658 ARP, Reply laptop.example.com is-at fe:54:00:1c:c4:1c (oui Unknown), length 28
14:04:18.184881 ARP, Request who-has web tell pcs-a, length 28
14:04:19.186844 ARP, Request who-has web tell pcs-a, length 28
14:04:22.611597 ARP, Request who-has pcs-c tell pcs-b, length 28
14:04:22.611680 ARP, Reply pcs-c is-at 52:54:00:b2:79:04 (oui Unknown), length 28
...

Test the cluster

Once I had updated the IP address,
pcs resource update VirtualIP IPaddr2 nic=ens8  ip=192.168.122.141 cidr_netmask=24 --group apachegroup 
I ran the following while also relading web.example.com in my browser.
watch -n 1 "ip a s ens8; df -h"
and did command like the following.
pcs cluster standby pcs-b.example.com
pcs cluster unstandby pcs-b.example.com
So I could watch the cluster resources move from hosts that suddently
did not have them:
[root@pcs-b ~]# ip a s ens8; df -h 
2: ens8:  mtu 1500 qdisc pfifo_fast state UP qlen 1000
    link/ether 52:54:00:8f:19:c6 brd ff:ff:ff:ff:ff:ff
Filesystem             Size  Used Avail Use% Mounted on
/dev/mapper/rhel-root   15G  1.2G   14G   8% /
devtmpfs               489M     0  489M   0% /dev
tmpfs                  498M   39M  459M   8% /dev/shm
tmpfs                  498M  6.6M  491M   2% /run
tmpfs                  498M     0  498M   0% /sys/fs/cgroup
/dev/vda1              497M  136M  362M  28% /boot
[root@pcs-b ~]# 
To hosts that did have them:
[root@pcs-a ~]# ip a s ens8; df -h
2: ens8:  mtu 1500 qdisc pfifo_fast state UP qlen 1000
    link/ether 52:54:00:7d:40:6b brd ff:ff:ff:ff:ff:ff
    inet 192.168.122.141/24 scope global ens8
       valid_lft forever preferred_lft forever
Filesystem               Size  Used Avail Use% Mounted on
/dev/mapper/rhel-root     15G  1.2G   14G   8% /
devtmpfs                 489M     0  489M   0% /dev
tmpfs                    498M   54M  444M  11% /dev/shm
tmpfs                    498M  6.6M  491M   2% /run
tmpfs                    498M     0  498M   0% /sys/fs/cgroup
/dev/vda1                497M  136M  362M  28% /boot
/dev/mapper/my_vg-my_lv  969M  2.5M  900M   1% /var/www
[root@pcs-a ~]# 




Sunday, August 3, 2014

Video: Troubleshooting Neutron Virtual Networks

This video on Troubleshooting Neutron Virtual Networks is really helpful for debugging OpenStack network problems. Make sure you understand TUN/TAP devices before watching.

Saturday, July 19, 2014

qpid101

#!/usr/bin/env python
# Filename:                qpid101.py
# Description:             Qpid 101
# Supported Langauge(s):   Python 2.7.x
# Time-stamp:              <2014-07-19 01:04:29 someone> 
# -------------------------------------------------------
# * What's qpid?
# It's like a queue from data structures 101. How do you share
# the queue between two or more separate programs on two more
# more separate computers? Forget about passing pointers and
# sharing namespaces. Instead use a network queue server with  
# its own protocol. Then imagine special queues like how in data
# structures class there were special trees and make this server
# support those special extensions to a traditional queue. 
#
# * Could I use something other than a queue?
# Sure, you could use a DB or a RESTful web-service but DBs are 
# better at storing data, not passing it. A web-service between
# two programs doesn't allow for the message to be queued for
# later if the other program is down; i.e. it is not asynchronous.
# Why not make something new and focus on what it should do well? 
# See http://qpid.apache.org/overview.html for more justification.
# 
# * How how do I use it? 
# In a 101-level nutshell follow the examples below from:
# https://qpid.apache.org/releases/qpid-trunk/programming/book
# 
# * How do I install the Python library? 
# On Fedroa: 'yum install python-qpid python-qpid_messaging' 
#
# * Need a qpid server? 
# The swat-a-fly-with-a-telephone-pole-method...
# If you have an openstack install around, allow the client (to be
# written below) to connect to your openstack server on port 5672.
# -------------------------------------------------------
import sys
from qpid.messaging import *
broker =  "192.168.122.28:5672"
address = "amq.topic"
connection = Connection(broker)
try:
    # establish connection to message broker
    connection.open()
    # create a session object on which messages can be sent/recv'd
    session = connection.session()
    # create a sender that sends to the given address
    sender = session.sender(address)
    # create a receiver receives messages from the given address
    receiver = session.receiver(address)
    # send the message
    sender.send(Message("Hello world!"));
    # receive the message (only wait 1 second, else wait forver)
    message = receiver.fetch(timeout=1)
    print message.content
    # ack receipt of message during session
    session.acknowledge()
except MessagingError,m:
    print m
finally:
    connection.close()
# -------------------------------------------------------
# Not too exciting on the client: 
#     sh-4.2$ ./qpid101.py 
#     Hello world!
#     sh-4.2$ 
#
# Let's look at the server:
# * 'yum install qpid-tools'
# 
# * Run 'qpid-printevents' and re-run qpid101.py 
# ** The following is the output; all lines from one run; fields trimmed
# 
# 1. clientConnect broker=localhost:5672 rhost=192.168.122.28:5672-192.168.122.1:33254 user=anonymous
# 2. queueDeclare broker=localhost:5672 rhost=192.168.122.28:5672-192.168.122.1:33254 user=anonymous
#      qName=41a24690-c85b-4d4b-97c4-cdfc4ae0664a:0.0 durable=F excl=T autoDel=T args={} disp=created
# 3. bind broker=localhost:5672 rhost=192.168.122.28:5672-192.168.122.1:33254 user=anonymous exName=amq.topic
#      qName=41a24690-c85b-4d4b-97c4-cdfc4ae0664a:0.0 key=# args={}
# 4. subscribe broker=localhost:5672 rhost=192.168.122.28:5672-192.168.122.1:33254 user=anonymous
#      qName=41a24690-c85b-4d4b-97c4-cdfc4ae0664a:0.0 dest=0 excl=F args={}
# 5. unsubscribe broker=localhost:5672 rhost=192.168.122.28:5672-192.168.122.1:33254 user=anonymous dest=0
# 6. queueDelete broker=localhost:5672 rhost=192.168.122.28:5672-192.168.122.1:33254 user=anonymous
#      qName=41a24690-c85b-4d4b-97c4-cdfc4ae0664a:0.0
# 7. clientDisconnect broker=localhost:5672 rhost=192.168.122.28:5672-192.168.122.1:33254 user=anonymous
#
# Try flooding it with 'while [ 1 ]; do ./qpid101.py > /dev/null ; done' and watch it run
# 
# * Run 'qpid-queue-stats' 
# ** It's a ~150 line Python program and prints the enq/deq rate every 10 seconds
#
# * Let's look at qpid-stat
# ** It's a ~500 line Python program that displays msgIn/MsgOut per queue and other stats
# ** Try 'watch "qpid-stat -q -S msgIn"' to see the queues sorted by messages in
# ** run '/etc/init.d/qpidd restart' as root in another termainal and watch the msgIn reset
# 
# queue      dur  autoDel  excl  msg   msgIn  msgOut  bytes  bytesIn  bytesOut  cons  bind
# =========================================================================================
# q-plugin                          0  25.3k  25.3k      0   19.2m    19.2m        1     2
# 2dcb1680...              Y        0  15.3k  15.3k      0   5.70m    5.70m        1     2
# conductor                         0  7.66k  7.66k      0   8.68m    8.68m        1     2
# ...
#
# ** Try 'watch "qpid-stat -c -I"' to pick out who is connecting
# *** Use the first 10 lines of code via a Python REPL and wrap the send in loop
# *** Pick out your client address and note the change in idle, and msgIn
# 
# client-addr                 cproc          cpid   connected   idle    msgIn  msgOut
# ===================================================================================
# :5672-192.168.122.1:41724                  21772  8m 25s      3m 30s   120k  40.2k
# :5672-192.168.122.28:54254  cinder-volume  15803  9h 18m 17s  10s     4.72k  5.02k
# ...
# 

Sunday, May 18, 2014

hello docker

I tried out Docker for the first time. I got the hello world node.js web app running as well as the RHEL7 Release Candidate Docker Image working. Fun.

Tuesday, February 25, 2014

subscription-manager cheat sheet

Red Hat is moving from RHN Classic to Red Hat Subscription Management so you may want to start using subscription-manager instead of rhn_register. Here's a cheat sheet on how to register your system to the CDN, attach to what should be the appropriate channel, see other repos and add an extra repo. For more info see the documentation.

subscription-manager register
subscription-manager attach --auto
subscription-manager list --available
subscription-manager repos --enable=rhel-6-server-optional-rpms

Friday, February 14, 2014

ksar wrapper

I have been using ksar to analyze the output of sar. I often use it one specific way so I have a wrapper script which assumes you've done the following:

  1. Download kSar-X.y.z.zip from sourceforge
  2. Unpack it in /usr/local/ksar/
  3. ln -s /usr/local/ksar/kSar-X.y.z /usr/local/ksar/ksar
  4. Add the following script somewhere in your $PATH
#!/bin/bash
# Filename:                ksar
# Description:             Wrapper to run ksar
# Supported Langauge(s):   GNU bash, version 4.2.x
# Time-stamp:              <2014-02-14 16:12:18 someguy> 
# -------------------------------------------------------
# http://sourceforge.net/projects/ksar/
# -------------------------------------------------------
if [ "$#" -eq 1 ]
then
    /bin/java -jar /usr/local/ksar/ksar/kSar.jar -input $1  -outputPDF $1.pdf
else
    echo "Usage: 'ksar foo.sar'";
    echo "where foo.sar is a sar file produced by sar";
    echo "Note that such a file can be found in an sosreport";
fi
The assumption is that you just want to generate a PDF for a particular sar report
$ ksar sar11 
time to parse: 560ms number of line: 2527 line/msec: 4.0
$ 
$ mupdf sar11.pdf & 

Monday, January 27, 2014

RDO quickstart

Tonight I installed OpenStack with RDO Quickstart on RHEL6. They've really made it easy.

Friday, January 24, 2014

rhel7 beta ifconfig is gone; now what?

My minimally installed rhel7 beta system didn't include ifconfig.
[root@rhel7 ~]# ifconfig 
-bash: ifconfig: command not found
[root@rhel7 ~]# 
You can get it with the net-tools package:
yum install net-tools
but the Networking Guide makes no mention of it and seems to encourage nmcli. If you need to get your system online quickly and are unable to get net-tools installed here's a nmcli crash course.

Edit /etc/sysconfig/network-scripts/ifcfg-ethX, like usual, and bring the device up with:

nmcli con up eth0
or bring it down with:
nmcli con down eth0
You can go as short as "nmcli c u eth0" or as long as "nmcli connection up eth0".

To see your active connections:

nmcli c s a
or do the long version:
[root@rhel7 ~]# nmcli connection show active
NAME  UUID                                  DEVICES  DEFAULT  VPN  MASTER-PATH 
eth0  5a26138b-99c5-4769-a036-7856598e696e  eth0     yes      no   --          
[root@rhel7 ~]# 

To then zoom in on the details of one of the devices see:

nmcli d sh eth0
or again do the long version and you'll get a lot more info:
[root@rhel7 ~]# nmcli device show eth0 | wc -l
55
[root@rhel7 ~]# 
So you might want to grep for just the IP or Mac:
[root@rhel7 ~]# nmcli d sh eth0 | grep ip
IP4.ADDRESS[1]:                         ip = 192.168.122.70/24, gw = 192.168.122.1
DHCP4.OPTION[6]:                        ip_address = 192.168.122.70
IP6.ADDRESS[1]:                         ip = fe80::5054:ff:fe5f:edfa/64, gw = ::
[root@rhel7 ~]# 

[root@rhel7 ~]# nmcli d sh eth0 | grep -i hw
GENERAL.HWADDR:                         52:54:00:5F:ED:FA
[root@rhel7 ~]# 

For bonus points, set your hostname using hostnamectl.

hostnamectl set-hostname $hostname
But don't forget to edit /etc/hosts.

Monday, January 20, 2014

The Slab

Today I learned about memory management via slab allocation and slabtop along with the move to slub after 2.6.23.

Wednesday, November 27, 2013

Old Thinkpad X40

In 2005 I taught a Python class and made enough money to buy myself an IBM (pre-Lenovo) Thinkpad X40:

  IBM ThinkPad X40 2371
  Dimensions (WxDxH) 10.6 in x 8.3 in x 1.1 in
  Weight 2.6 lbs
  Processor Intel Pentium M 713 1.1 GHz ULV
  Cache Memory 1 MB - L2 cache
  RAM 256 MB (installed) / 1.28 GB (max) - DDR SDRAM - PC2700 - 333 MHz
  W7P9838   FREE 512MB PC2700 CL2.5 NP DDR  $0.00
  Hard Drive 30 GB - 4200 rpm
  Display 12.1" TFT active matrix XGA (1024 x 768) - 24-bit (16.7 million colors)
  Telecom Fax / modem - CDC - 56 Kbps
  Networking Network adapter - Ethernet, Fast Ethernet, Gigabit Ethernet, IEEE 802.11b, IEEE 802.11g
  IBM 11 A/B/G WIRELESS LAN MINI PCI ADAPTER II 

I am in a state where I'll be between laptops so I've installed an XFCE spin of Fedora 20 Beta RC 5 and I'm surprised how usable it is. I bought a new battery (IBM-22-4400) from laptopbatterydepot.com so I can travel with it and I'm getting decent battery life (~5 hours so far) but my bottle neck is disk IO. sweclockers.com has a nice write up on installing $15 PATA to mSata adaptor along with a ~$100 SSD on the exact same model of laptop to speed it up and increase the reliability of an 8 year old battery that doesn't owe anyone anything anymore. Now the temptation sets in. Do I want to spend ~$115 and a few hours on an old laptop?

Saturday, November 9, 2013

My Puppet Module Writing Environment

I previously documented my Foreman/Puppet environment. In this entry I am documenting how I set things up so I could comfortably develop puppet modules inside of this environment. This includes the writing of a very simple module to make /etc/hosts the same on all of the hosts in my environment. This hosts module itself reinvents the wheel as there are many more advanced puppet forge modules for doing this. I am only trying to solve a simple problem for an environment exclusive to my own. What I hope is more relevant for this entry is how the development tools were set up and the conventions being used.

Puppet Mode for Emacs

I am planning to write my manifests locally in emacs so I will get puppet mode for emacs.


$ cd ~/elisp/
$ git clone http://github.com/puppetlabs/puppet-syntax-emacs.git
Cloning into 'puppet-syntax-emacs'...
remote: Counting objects: 63, done.
remote: Compressing objects: 100% (46/46), done.
remote: Total 63 (delta 20), reused 55 (delta 17)
Unpacking objects: 100% (63/63), done.
$ 
Add the following to ~/.emacs and evaluate the following S-expressions:
(add-to-list 'load-path "~/elisp/puppet-syntax-emacs")
(setq py-install-directory "~/elisp/puppet-syntax-emacs")
(require 'puppet-mode)


Set up versioning for a module

At work we use an authoritative (by convention) git depot. I also keep a personal authoritative git depot on a vps on the Internet so I will create a bare repository there but which depot I use is arbitrary.

ssh me.xen.prgmr.com
cd /opt/git/
mkdir hosts.git
cd hosts.git
git init --bare
logout
I will then clone this empty repository down into a place on my laptop to work:
$ cd ~/src/puppet/hosts
$ git clone me@me.xen.prgmr.com:/opt/git/hosts.git
Cloning into 'hosts'...
warning: You appear to have cloned an empty repository.
$ 

Write a simple module to manage /etc/hosts

Inside the empty repository that I cloned I will create the module:

$ mkdir -p hosts/{files,templates,manifests}
$ touch hosts/manifests/init.pp
$ mkdir -p hosts/templates
$ touch templates/hosts.erb 
Put the following in hosts/manifests/init.pp:
class hosts {
  file { "/etc/hosts":
    ensure => file,
    owner => "root",
    group => "root",
    mode => 0644,
  }
  File['/etc/hosts'] {
    content => template('hosts/hosts.erb'),
  }
}
Put the following in templates/hosts.erb:
127.0.0.1 localhost.localdomian localhost 
127.0.0.1 <%= @fqdn %> <%= @hostname %>
# continue with other hosts as you see fit

Commit the changes and push them back to the depot:

$ cd ~/src/puppet/hosts
$ git add *
$ git status
# On branch master
#
# Initial commit
#
# Changes to be committed:
#   (use "git rm --cached ..." to unstage)
#
# new file:   templates/hosts.erb
# new file:   manifests/init.pp
#
$ git commit -m "first version of hosts"
[master (root-commit) 524416d] first version of hosts
 2 files changed, 23 insertions(+)
 create mode 100644 hosts/templates/hosts.erb
 create mode 100644 hosts/manifests/init.pp
$ 
$ git push origin master
Counting objects: 8, done.
Delta compression using up to 4 threads.
Compressing objects: 100% (4/4), done.
Writing objects: 100% (8/8), 754 bytes, done.
Total 8 (delta 0), reused 0 (delta 0)
To me@me.xen.prgmr.com:/opt/git/hosts.git
 * [new branch]      master -> master
$ 


On the Puppet Server

A note about some conventions on my set up. My Puppet server has /etc/puppet/modules but it's a symlink to environments/common and I have used an ACL to give myself access to it so I can radiply make changes without being root (this will come in handy later when we talk about how I speed up the cycle):

[root@puppet puppet]# pwd
/etc/puppet
[root@puppet puppet]# ls -l modules
lrwxrwxrwx. 1 root root 19 Sep 24 02:10 modules -> environments/common
[root@puppet puppet]# cd environments
[root@puppet environments]# setfacl -m u:me:rwx common/
[root@puppet environments]# 

Import the new module into the puppet server from git:
[me@puppet ~]$ cd /etc/puppet/modules/
[me@puppet modules]$ git clone me@me.xen.prgmr.com:/opt/git/hosts.git 
Initialized empty Git repository in /etc/puppet/environments/common/hosts/.git/
remote: Counting objects: 8, done.
remote: Compressing objects: 100% (4/4), done.
Receiving objects: 100% (8/8), done.
remote: Total 8 (delta 0), reused 0 (delta 0)
[me@puppet modules]$ 

On Foreman

Login to the Foreman web interface and navigate to the puppet classes:
More > Configuration > Puppet Classes

Click "Import from puppet". You should then see the class name "hosts". Select it and then you should see something like the following:
















Select the environments you want to import the new module into and then select "Update". Once the module is imported, select the servers you want to have the new module. For example, you can apply the new module to the puppet-agent client.


On a Puppet client
From the puppet-agent server you can test the new configuration and observe the change.


[root@puppet-agent ~]# md5sum /etc/hosts
b259bdfd326846029c25beff10fc5ac6  /etc/hosts
[root@puppet-agent ~]# puppet agent
[root@puppet-agent ~]# puppet agent --test
Info: Retrieving plugin
Info: Caching catalog for puppet-agent.example.com
Info: Applying configuration version '1384016280'
Notice: Finished catalog run in 0.29 seconds
[root@puppet-agent ~]# md5sum /etc/hosts
fd34f5442b4209a472d6ea8ac8e24d77  /etc/hosts
[root@puppet-agent ~]# 

Speeding up the Cycle
I am used to having a git depot at work so I've shown how to stick one in the middle of the cycle but to do a commit and update for each revision of code is not practical. To get around this I can write code on the puppet VM itself (I am the only user for this environment), but use a local editor on my laptop. I prefer to do this with emacs tramp and have the following to my .emacs:


(setenv "puppet" "/ssh:me@puppet.example.com:/etc/puppet/modules/")

These conventions could be applied when developing puppet modules in a development environment like the one described here and then applied to a production environment. For example, I could write a module in this development puppet environment and check it into the git depot at work as I reached the appropriate points in my development. When ready it could then get checked into the puppet modules directory on a production puppet server but in the staging or QA environment of that puppet server (see Chapter 3 of Pro Puppet) so it could be further tested before getting pushed into production. The assumed goal here is that you would want to develop a puppet module without needing to be tethered to the servers at work.

Update: a previous version of this post had a bug as /etc/host was in files and not a template. This would not work correctly has there would be no reference to the the loopback device relative to the hostname. The need for a variable here means you need a template, not a file.


Sunday, November 3, 2013

Foremen Environment Inside VirtualBox

This entry describes how I set up an environment on my laptop to learn Foreman. There are other ways to do this but this worked for me. My goal was to be able to use Foreman to provision VMs with no intervention beyond turning the VM on and using only VirtualBox as my hypervisor. I also wanted to be able to do provision CentOS systems with my laptop not connected to the Internet (e.g. suppose I am on an airplane). As a result of these goals there are a variety of network tweaks to set this up which required some reading of Chapter 6 of the VirtualBox manual.


Network Overview

Foreman provisions systems using PXE, which requires DHCP and TFTP. Foreman can take care of TFTP but in this case I am running a separate machine for DHCP which is also acting as a router for the VMs inside my environment. I am calling this machine netman (short for network manager). I am using a couple of NAT hacks to make this work for the situations I will encounter; e.g. if Foreman tells a VM to pull a repo from the Internet, assuming dom0 is on the network. I am also keeping a local CentOS mirror for when not on the Internet. Here is a diagram of the network ranging from my laptop's wifi connection down into VirtualBox and then into the VMs.

















Here is a list of systems and their configurations.

1. dom0 this my laptop running VirtualBox. It has two network devices:

* en0
** Wifi card which is DHCP'd some NAT'd address depending my location
** let's assumue 192.168.1.10x (RFC1918-C)

* vboxnet1
** This is how my laptop communicates with my VMs
** It is made by VirtualBox: VirtualBox > Preferences > Network > +
** In this case I specified 172.16.1.1/24 with no DHCP server  
** I am using a subset of RFC1918-B for this network

2. netman this is my network manager VM. It has two devices:

* eth0:
** In vbox terms it is Adapter 1, running NAT to reach the Internet
** vbox DHCPs to eth0 from a subset of RFC1918-A with gateway 10.0.2.2 
** /etc/sysconfig/network-scripts/eth0 is configured for DHCP

* eth1:
** In vbox terms it is Adapter 2, running Host-only Networking
** The vbox name it uses is vboxnet1 
** /etc/sysconfig/network-scripts/eth1 is statically configured
*** 172.16.1.2 

netman acts as a router and DHCP server to hosts within 172.16.1.0/24. It uses iptables masquerading to NAT for those hosts so their Internet bound packets can come in eth1 and go out eth0. It is true that eth0 was itself NAT'd by VirtualBox and might then go out wifi which itself was probably NAT'd. So we have some ugly NAT hacks within the three regions of RFC1918 space: B > A > C. It is ugly, but within my virtual box environment for learning, I found this sufficient to achieve my goals.


3. client this could be any VM within my environment

* eth0:
** In vbox terms it is Adapter 1, running Host-only Networking
** The vbox name it uses is vboxnet1 
** /etc/sysconfig/network-scripts/eth1 is configured for DHCP
** IP Address is DHCP'd from within 172.16.1.3/24
** Gateway is DHCP'd to 172.16.1.2 (eth1 of netman)
** DNS handed out by DHCP server is google's 8.8.8.8, 8.8.4.4
** Static IPs are assigned by mac address
** dom0 can use it's vboxnet1 device (172.16.1.1) to SSH to client


Configure dom0

Download and install VirtualBox. Virtual machines cannot PXE boot unless you install the extpack:
$ VBoxManage extpack install Oracle_VM_VirtualBox_Extension_Pack-4.2.18-88780.vbox-extpack 
0%...10%...20%...30%...40%...50%...60%...70%...80%...90%...100%
Successfully installed "Oracle VM VirtualBox Extension Pack".
$ 

Create the vboxnet1 network: VirtualBox > Preferences > Network > +

























Configure the IP 172.16.1.1 and netmask 255.255.255.0 and leave DHCP disabled since netman will be the DHCP server.

Configure netman

I keep a minimally installed RHEL6 VM within VirtualBox to clone, so I cloned that into netman.

Add two network devices

Configure Adapter 1 for NAT:





















Configure Adapter 2 for Host-only networking using vboxnet1:



























Start netman with virtualbox. As eth0 and eth1 try to come up you might see a message like:

Device eth0 does not seem to be present, delaying initialization
Edit /etc/sysconfig/network-scripts/ifcfg-eth{0,1} to set the correct MAC addresses and then run:
rm /etc/udev/rules.d/70-persistent-net.rules
udevadm trigger
Configure eth0 for DHCP:
[root@netman ~]# cat /etc/sysconfig/network-scripts/ifcfg-eth0
DEVICE=eth0
ONBOOT=yes
BOOTPROTO=dhcp
HWADDR=08:00:27:20:C2:8E
[root@netman ~]# 
Configure eth1 with a static address and to act as a router:
[root@netman ~]# cat /etc/sysconfig/network-scripts/ifcfg-eth1
BOOTPROTO=none
PEERDNS=yes
HWADDR=08:00:27:62:E4:C1
TYPE=Ethernet
IPV6INIT=no
DEVICE=eth1
NETMASK=255.255.255.0    
BROADCAST=""
IPADDR=172.16.1.2     # Gateway of the LAN
NETWORK=172.16.1.0 
USERCTL=no
ONBOOT=yes
[root@netman ~]# 
Configure iptables so it will NAT
# Flush the firewall
iptables -F
iptables -t nat -F
iptables -t mangle -F
iptables -X
iptables -t nat -X
iptables -t mangle -X

# Set up IP FORWARDing and Masquerading
iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE
iptables -A FORWARD -i eth1 -j ACCEPT

# Enable packet forwarding by kernel 
# You should save this setting in /etc/sysctl.conf too
echo 1 > /proc/sys/net/ipv4/ip_forward

# Apply the configuration
service iptables save
service iptables restart

Configure a DHCP server

This borrows from RedHat's guide.

Install the DHCP server:

yum install dhcp

Configure /etc/dhcp/dhcpd.conf with something like this
option domain-name "example.com";
option domain-name-servers 8.8.8.8, 8.8.4.4;

default-lease-time 600;
max-lease-time 7200;

log-facility local7;

subnet 172.16.1.0 netmask 255.255.255.0 {
  range 172.16.1.13 172.16.1.23;
  option routers 172.16.1.2;
}

host client {
  hardware ethernet 08:00:27:59:59:33;
  fixed-address 172.16.1.3;
}
A few things of note:
  • I'm using Google's Public DNS servers since it works well everywhere I have been and is sufficient for a test environment.
  • I have a range of IPs I will hand out between 172.16.1.13 and 172.16.1.23;
  • Hosts that I provision are also getting static assignments by MAC address; e.g. I have an entry for the MAC address of "client" which I will set up next.

Start dhcpd and configure it to start on boot.

/etc/init.d/dhcpd start
chkconfig dhcpd on

Configure a static CentOS Mirror

netman will serve this mirror over HTTP so install Apache.

yum install httpd
service httpd start
chkconfig httpd on

Next we will get content to serve the mirror.

  • Download an
    CentOS
    ISO
    file and store it somewhere on dom0.
  • Within Virtualbox's entry for netman go to Settings > Storage
    and then add a virtual IDE device which is the ISO file.
Next we will mount the ISO file under the web tree. To do this I made
a directory:
mkdir -p /var/www/html/6.4/os/x86_64
and then put the following in my /etc/fstab:
/dev/cdrom1  /var/www/html/6.4/os/x86_64 iso9660 ro 0 0

After a "mount -a", I was then able to point a browser at http://172.16.1.2/6.4/os/x86_64/ and see what I expected to see. The mirror of a static ISO might get out of date but will get the system booted if it doesn't have Internet access and I could yum upgrade it later.


Configure Client

I am showing the network configuration as it would apply to a generic RHEL6 or CentOS6 box. Later we will let Foreman provision these configurations.

Set up a basic RHEL6 box (perhaps from a clone) and configure Adapter 1 as a Host-only Adapter using the vboxnet1 network. Boot the system and configure configure eth0 for DHCP:

[root@client ~]# cat /etc/sysconfig/network-scripts/ifcfg-eth0 
DEVICE=eth0
ONBOOT=yes
BOOTPROTO=dhcp
HWADDR=08:00:27:59:59:33
[root@client ~]# 

Restart networking and "tail -f /var/log/messages" on netman to see your client boot and get an address from your DHCP server.

Oct 18 18:00:41 netman dhcpd: DHCPREQUEST for 172.16.1.3 from 08:00:27:59:59:33 via eth1
Oct 18 18:00:41 netman dhcpd: DHCPACK on 172.16.1.3 to 08:00:27:59:59:33 via eth1

If you can then SSH into the client and resolve Internet hosts from the client, then you have a working network configuration and can move on to configuring Foreman.


Set up some form of DNS (even a hack)


Until I set up a DNS server in my environment my /etc/hosts file on all my systems for this environment contain the following:
172.16.1.1   laptop laptop.example.com
172.16.1.2   netman netman.example.com
172.16.1.3   client client.example.com
172.16.1.4   puppet puppet.example.com
172.16.1.4   foreman foreman.example.com
172.16.1.5   unattended unattended.example.com
Note that, we will configue 172.16.1.4 next and that it will run Puppet and Foreman. At the end we'll configure unattended which will be an unattended install.

Build a host to run Puppet/Foreman

Next I will configure a server running Puppet and Foreman. I will start with a RHEL6 host configured on the network as described above with the generic client. I called my host puppet. Remember to create an entry in netman for DHCP in dhcpd.conf:

host puppet {
  hardware ethernet 08:00:27:B9:B3:E8;  
  fixed-address 172.16.1.4;
}

If you booted this host and got a DHCP'd address from the range being handed out and want to static it as described above, then you can revert the lease file as described below.

cd /var/lib/dhcpd/
mv dhcpd.leases~ dhcpd.leases
service dhcpd restart

Once you have your a booting vanilla RHEL6 box you can install Puppet and Foreman.

First install EPEL

wget https://dl.fedoraproject.org/pub/epel/6/x86_64/epel-release-6-8.noarch.rpm
yum localinstall epel-release-6-8.noarch.rpm

Install Puppet from their repos. To do this I added the following to /etc/yum.repos.d/puppet.repo

[Puppet]
name=Puppet
baseurl=http://yum.puppetlabs.com/el/6Server/products/x86_64/
gpgcheck=0

[Puppet_Deps]
name=Puppet Dependencies
baseurl=http://yum.puppetlabs.com/el/6Server/dependencies/x86_64/
gpgcheck=0
Install Puppet:
yum -y install puppet facter puppet-server

Then run the Foreman Installer

yum -y install http://yum.theforeman.org/releases/1.3/el6/x86_64/foreman-release.rpm
yum -y install foreman-installer
Answer yes to all defaults. Near the end you should see something like:
...
Notice: Finished catalog run in 294.38 seconds

 Okay, you're all set! Check
/usr/share/foreman-installer/foreman_installer/answers.yaml for your
config.

 You can apply it in the future as root with:
  echo include foreman_installer | puppet apply --modulepath
/usr/share/foreman-installer -v
# 

Once Foreman is running you should be able to login to its web interface at https://puppet.example.com. Read the documentation on what the default username/password is. Once you're in, configure Foreman with a smartproxy.

GUI > More > Configuration > Smart Proxies

Make sure the FQDN in /etc/hosts is consistent with the FQDN as defined in /etc/foreman-proxy/settings.yml.


Configure a client to be managed by Puppet


As described in the client section above, set up a basic RHEL6 box (perhaps from a clone) and configure Adapter 1 as a Host-only Adapter using the vboxnet1 network. Take note of its mac address so you can put an entry in netman's DHCP server. Boot the system and configure configure eth0 for DHCP.


First install EPEL

wget https://dl.fedoraproject.org/pub/epel/6/x86_64/epel-release-6-8.noarch.rpm
yum localinstall epel-release-6-8.noarch.rpm

Install Puppet from their repos. To do this I added the following
to /etc/yum.repos.d/puppet.repo

[Puppet]
name=Puppet
baseurl=http://yum.puppetlabs.com/el/6Server/products/x86_64/
gpgcheck=0

[Puppet_Deps]
name=Puppet Dependencies
baseurl=http://yum.puppetlabs.com/el/6Server/dependencies/x86_64/
gpgcheck=0

Install ruby, puppet, and facter:

yum install ruby ruby-libs puppet facter

Now that puppet, the agent, should be installed, add "server=puppet.example.com" to /etc/puppet/puppet.conf. Then run "puppet agent --server=puppet.example.com --no-daemonize --verbose" and observe:

[root@puppet-agent ~]# puppet agent --server=puppet.example.com --no-daemonize --verbose
info: Creating a new SSL key for puppet-agent.example.com
info: Caching certificate for ca
info: Creating a new SSL certificate request for puppet-agent.example.com
info: Certificate Request fingerprint (md5): EA:5E:82:B8:A8:BA:8D:7E:5B:3D:20:2C:68:06:B0:04
...
notice: Did not receive certificate
...

The above sends a cert to be signed by the master. It will check every two minutes if there is a new cert.

Back on puppet.example.com, sign the certificate:

[root@puppet ~]# puppet cert --list
  "puppet-agent.example.com" (EA:5E:82:B8:A8:BA:8D:7E:5B:3D:20:2C:68:06:B0:04)
[root@puppet ~]# 

[root@puppet-master public_keys]# puppet cert --sign puppet-agent.example.com
notice: Signed certificate request for puppet-agent.example.com
notice: Removing file Puppet::SSL::CertificateRequest puppet-agent.example.com at '/var/lib/puppet/ssl/ca/requests/puppet-agent.example.com.pem'
[root@puppet-master public_keys]# 

Then on puppet-agent continue to observe the output of "puppet agent --server=puppet.example.com --no-daemonize --verbose". You should see news of the cert's acceptance:

...
info: Caching certificate for puppet-agent.example.com
notice: Starting Puppet client version 2.6.18
info: Caching certificate_revocation_list for ca
info: Caching catalog for puppet-agent.example.com
info: Applying configuration version '1369603392'
info: Creating state file /var/lib/puppet/state/state.yaml
notice: Finished catalog run in 0.01 seconds
^c

Now run the puppet agent as a daemon and configure it to run on boot:

[root@puppet-agent ~]# service puppet start
Starting puppet:                                           [  OK  ]
[root@puppet-agent ~]# chkconfig puppet on
[root@puppet-agent ~]#

You can see how your agent is doing anytime:

[root@puppet-agent ~]# puppet agent --test
Info: Retrieving plugin
Info: Caching catalog for puppet-agent.example.com
Info: Applying configuration version '1383503512'
Notice: Finished catalog run in 0.03 seconds
[root@puppet-agent ~]# 

Now that your host is in puppet, you should be able to see it in the Foreman web GUI under Hosts.


Install a module from the puppet forge and push it to puppet-agent


On the puppet master, install jeffmccune/motd.

[root@puppet ~]# cd /usr/share/puppet/modules
[root@puppet modules]# puppet module install jeffmccune/motd
Installed "jeffmccune-motd-1.0.3" into directory: motd
[root@puppet modules]# ls
motd
[root@puppet modules]# 
On the same puppet master, define $puppetserver and node list in /etc/puppet/manifests/site.pp

import 'nodes.pp'
$pupppetserver = 'puppet.example.com'
Define the nodes in /etc/puppet/manifests/nodes.pp:
node 'puppet-agent.example.com' {
     include motd
}

In the above case I am making a place for my puppet-agent node and asking that the motd module be on the agent.


On the agent, wait the default amount of time (30 minutes) or reload puppet.

[root@puppet-agent ~]# service puppet reload
Restarting puppet:                                         [  OK  ]
[root@puppet-agent ~]# tail -f /var/log/messages
May 26 19:50:24 puppet-agent puppet-agent[13924]: Restarting with '/usr/sbin/puppetd '
May 26 19:50:25 puppet-agent puppet-agent[14060]: Reopening log files
May 26 19:50:25 puppet-agent puppet-agent[14060]: Starting Puppet client version 2.6.18
May 26 19:50:28 puppet-agent puppet-agent[14060]: (/File[/etc/motd]/content) content changed
'{md5}d41d8cd98f00b204e9800998ecf8427e' to '{md5}1b48863bff7665a65dda7ac9f57a2e8c'
May 26 19:50:28 puppet-agent puppet-agent[14060]: Finished catalog run in 0.02 seconds

Now if I SSH into the agent, I see my new MOTD.

$ ssh puppet-agent -l root
Last login: Sun Nov  3 13:44:16 2013 from dom0
-------------------------------------------------
Welcome to the host named puppet-agent
RedHat 6.4 x86_64
-------------------------------------------------
Puppet: 3.3.0
Facter: 1.7.3

FQDN: puppet-agent.example.com
IP:   172.16.1.6

Processor: Intel(R) Core(TM) i7-3667U CPU @ 2.00GHz
Memory:    490.63 MB

-------------------------------------------------
[root@puppet-agent ~]$ 

So far we have verified that puppet-agent is able to be managed from Puppet directly after editing nodes.pp on the Puppet server. If we were to now tell Foreman to manage this host and apply the same motd puppet module then we'd see an error. For example, in the GUI under hosts select the puppet-agent host and then:

Edit > Manage Host > Puppet Classes> motd > Sumbit 
While under Edit select "Manage host". If you were to then re-run puppet you'd see:
[root@puppet-agent ~]# puppet agent --test
Info: Retrieving plugin
Error: Could not retrieve catalog from remote server: 
Error 400 on SERVER: Duplicate declaration: Class[Motd] 
is already declared; cannot redeclare on node puppet-agent.example.com
Warning: Not using cache on failed catalog
Error: Could not retrieve catalog; skipping run
[root@puppet-agent ~]#
My goal is to have Foreman manage my host and Foreman maintains its list of hosts to manage by Puppet's ENC API. So rather than edit my nodes.pp on my Puppet server directly to tell Puppet which modules to apply to my hosts, I will remove the entry for motd so that my nodes.pp looks like the following:
node 'puppet.example.com' {
}
node 'puppet-agent.example.com' {
}
and then re-run my puppet test: 3
[root@puppet-agent ~]# puppet agent --test
Info: Retrieving plugin
Info: Caching catalog for puppet-agent.example.com
Info: Applying configuration version '1383513022'
Notice: Finished catalog run in 0.03 seconds
[root@puppet-agent ~]# 
The above works because I had told foreman to apply that motd module.

I can test this further by installing the puppetlabs/ntp module.

[root@puppet ~]# cd /usr/share/puppet/modules
[root@puppet modules]# ls
[root@puppet modules]# puppet module install puppetlabs/ntp -i common
Notice: Preparing to install into /etc/puppet/modules/common ...
Notice: Created target directory /etc/puppet/modules/common
Notice: Downloading from https://forge.puppetlabs.com ...
Notice: Installing -- do not interrupt ...
/etc/puppet/modules/common
└─┬ puppetlabs-ntp (v2.0.0-rc1)
  └── puppetlabs-stdlib (v4.1.0)
[root@puppet modules]# 
From there I can have the Foreman GUI tell puppet-agent to use that module.
More > Configuration > Puppet Classes > Import from $host > Update

I now see my class name of "ntp" with 19 keys. I click ntp and I could over ride settings; e.g. add the address of my NTP server. To apply that module to my NTP server I can use the Foreman GUI with:

Hosts > select your host > Edit > Puppet Classes
Then select one of the available classes like NTP and make it an included class and click submit. After you click submit you can have your host check in:
[root@puppet-agent ~]# puppet agent --test
Info: Retrieving plugin
Info: Caching catalog for puppet-agent.example.com
Info: Applying configuration version '1383513022'
Notice: /Stage[main]/Ntp/Service[ntpd]/ensure: ensure changed 'stopped' to 'running'
Info: /Stage[main]/Ntp/Service[ntpd]: Unscheduling refresh on Service[ntpd]
Notice: Finished catalog run in 1.47 seconds
[root@puppet-agent ~]# 

You can then view your host and see a report about it. If you click the yaml button, then you can see the config file that was applied to that host.


Configure a VM to be provisioned by Foreman


We'll now configure a VM which can be defined in virtualbox, given a DHCP reservation, defined in Foreman, such that the act of then turning the VM on alone, should result in that VM being PXE booted, having the OS installed and having a listening puppet agent which will apply all configurations you deem appropriate for the host.

Assuming you have the environment described above you should be able to set up something similar to what you see in Dominic Cleal's Foreman Quickstart: unattended installation screen cast.

Create a new virtual machine called "unattended" in virtualbox and configure Adapter 1 as a Host-only Adapter using the vboxnet1 network. Take note of its mac address so you can put an entry in netman's DHCP server. This host's DHCP entry should also contain the IP of the TFTP server as well as the file to request from the TFTP server that it can use to boot.

host unattended {
  hardware ethernet 08:00:27:3c:b0:a4;
  fixed-address 172.16.1.5;
  next-server 172.16.1.4;
  filename "pxelinux.0";
}
Next we have to define the host in Foreman so that it can configure it. First we need to define what components Foreman has to define that host starting with an operating system in the Foreman GUI:
More > Provisioning > Operating Systems 
Define a CentOS 6.4 OS. Then set up a Provisioning Template:
More > Provisioning > Provisioning Templates
Select "PXE Default" and edit the template to point to the local mirror we set up on netman's IP address:
"url --url http://172.16.1.2/6.4/os/x86_64/"
Looking over the script more you will see that the default is configured to install Puppet using EPEL. This is not as current as Puppet's repository but that can be fixed later. Under the Association tab, tick CentOS 6.4 and click Save.

Select "PXE Default PXELinux" and edit the Template so that the last five lines look as follows with a reference to the provisioning template on the Puppet server and click Save.

<% if @host.operatingsystem.name == "Fedora" and @host.operatingsystem.major.to_i > 16 -%>
append initrd=<%= @initrd %> ks=http://172.16.1.4/unattended/provision ks.device=bootif network ks.sendmac
<% else -%>
append initrd=<%= @initrd %> ks=http://172.16.1.4/unattended/provision ksdevice=bootif network kssendmac
<% end -%>
Under Association, assoctiate this template with CentOS 6.4 and click Save.

Associate a partition table with your CentOS 6.4 OS by selecting:

More > Provisioning > Provisioning Templates
and then selecting an Operating System Family as RedHat and then clicking Submit.

Define an operating system mirror by selecting:

More > Provisioning > Installation Media > CentOS Mirror
and then edit as follows:
Name: CentOS mirror
Path: http://172.16.1.2/$major.$minor/os/$arch/
Operating System Family: RedHat
Associate this mirror with the OS.
More > Provisioning > Operating Systems > CentOS 6.4
Tick the Architecture, Partition tables, Installation Media. The number the Templates file, set the Provision to "Kickstart Default" and PXELinux to "Kickstart Default PXELinux" and click Submit.

Define a subnet to build the host within Foreman. In this case I am going to enter the criteria of vboxnet1. From the Foreman GUI:

More > Provisioning > Subnets > New Subnet
Then define the subnet as follows:
Name: quickstart
Network Address: 172.16.1.0
Netmask: 255.255.255.0
Domain: example.com
TFTP Proxy: puppet
Leave the other fields blank and click Submit.

Define the host itself within the Foreman GUI:

Hosts > New Host  
Then define the host as follows:
Host Tab:
   Name: unattended
   Deploy on: Bare Metal
   Environment: Production
   Puppet CA: puppet
   Puppet Master: puppet
Operating System:
   Architecture: x86_64 
   Operating System: CentOS 6.4
   Media: CentOS Mirror
   Partition Table: RedHat Default
You will also need to fill in the items under the Network tab based on the entries you made for DHCP above. Finally click Submit to enter the host into Foreman's database and have Foreman contact the TFTP server smartproxy so that the smartproxy will load the TFTP server with the desired images to build this host.

Next you can check that the TFTP server entries were created properly. On the puppet master, /var/lib/tftpboot/pxelinux.cfg should contain a file whose name is the same as the mac address of unattended.example.com and /var/lib/tftpboot/pxelinux.cfg/boot should contain CentOS-6.4-x86_64-initrd.img and CentOS-6.4-x86_64-vmlinuz, which can be used to boot.

Now that you have defined the host in Foreman, test the TFTP server.

[root@netman ~]# tftp puppet
tftp> get pxelinux.0
tftp> quit
[root@netman ~]# ls -lh pxelinux.0 
-rw-rw-r--. 1 root root 27K Oct 19 13:46 pxelinux.0
[root@netman ~]# 

You should then be able to turn the host on and watch it PXE boot.














If you have trouble getting the host PXE booting see the Unattended Provisioning Troubleshooting flow chart or the Unattended Provisioning wiki entry.


Update 11/16/13: I forgot to mention to make sure you edit /etc/sudoers:

foreman-proxy ALL = NOPASSWD: /usr/bin/puppet cert *
Defaults:foreman-proxy !requiretty
and that /var/log/foreman-proxy/proxy.log is very helpful when debugging.

selinux authorized_keys context

IMHO, Googling "selinux authorized_keys context" produces too many results with restorecon. To me it makes more sense to just change the context directly.
chcon -t ssh_home_t .ssh/
chcon -t ssh_home_t .ssh/authorized_keys
If you want this context change to persist future runs of restorecon, then use
semanage.