By thomas, Fri, 04/03/2009 - 00:18
func is the Fedora Unified Network Controller, it can be found on fedorahosted.org. Func is written in python and can used in python scripts. For some examples of what func can do, look at the home page in the section "examples speak louder than words".

We already downloaded and installed func on our clients using kickstart. We can now install fun on server0 and push the configuration with puppet.

[root@server0 ~]# yum install func
Setting up Install Process
Parsing package install arguments
Resolving Dependencies
--> Running transaction check
---> Package func.noarch 0:0.24-1.el5 set to be updated
--> Processing Dependency: certmaster >= 0.24 for package: func
--> Processing Dependency: python-simplejson for package: func
--> Processing Dependency: pyOpenSSL for package: func
--> Running transaction check
...
Installed: func.noarch 0:0.24-1.el5
Dependency Installed: certmaster.noarch 0:0.24-1.el5 pyOpenSSL.x86_64 0:0.6-1.p24.7.2.2 python-simplejson.x86_64 0:2.0.3-2.el5
Complete!
[root@server0 ~]# chkconfig certmaster on
[root@server0 ~]# service certmaster start
Starting certmaster daemon: 
Certmaster listens on port 51235, we'll need to add that port to our firewall.
[root@server0 ~]# iptables -I RH-Firewall-1-INPUT -p tcp -m state --state NEW,ESTABLISHED,RELATED --dport 51235 -j ACCEPT
[root@server0 ~]# cd /etc/sysconfig
[root@server0 sysconfig]# iptables-save >iptables
Now, let's add the configuration for func to puppet and restart the puppetmaster (since we will need to change site.pp).
[root@server0 etc]# cd /var/lib/puppet/files/base/etc
[root@server0 etc]# mkdir certmaster
[root@server0 etc]# cd certmaster/
[root@server0 certmaster]# vi minion.conf
[root@server0 certmaster]# cat minion.conf
# configuration for minions

[main]
certmaster = server0.example.com
certmaster_port = 51235
log_level = DEBUG
cert_dir = /etc/pki/certmaster
[root@server0 certmaster]# cd /etc/puppet/manifests/
[root@server0 manifests]# vi func.pp
[root@server0 manifests]# cat func.pp
class func {
	remotefile { "/etc/certmaster/minion.conf": mode => 644 }
	service { funcd: ensure => true, enable => true, hasrestart => true }
}
[root@server0 manifests]# vi site.pp
[root@server0 manifests]# cat site.pp
# site.pp
import "functions.pp"

# define nodes
import "nodes.pp"

# define classes
import "base.pp"
import "func.pp"
[root@server0 manifests]# vi nodes.pp 
[root@server0 manifests]# cat nodes.pp
node default {
	include base
	include func
}
[root@server0 manifests]# service puppetmaster restart
Stopping puppetmaster:                                     [  OK  ]
Starting puppetmaster:                                     [  OK  ]
Now on our client, if we restart puppet, we can see the change propagated (we could also wait and the change would be propagated at the next check-in.
[root@client15 ~]# cd /etc/certmaster
[root@client15 certmaster]# puppetd --fqdn=$HOSTNAME --test --no-splay --server=server0.example.com --onetime --verbose --factsync
info: Retrieving facts
info: Caching catalog at /var/lib/puppet/localconfig.yaml
notice: Starting catalog run
4c4
 certmaster = certmaster
---
> certmaster = server0.example.com
8d7
 
notice: //Node[default]/func/Remotefile[/etc/certmaster/minion.conf]/File[/etc/certmaster/minion.conf]/source: replacing from source puppet://server0.example.com/base//etc/certmaster/minion.conf with contents {md5}7c1ae4564f05e3f9ca50633c89d2a63b
notice: //Node[default]/func/Service[funcd]/ensure: ensure changed 'stopped' to 'running'
notice: Finished catalog run in 2.67 seconds
[root@client15 certmaster]# cat minion.conf 
# configuration for minions

[main]
certmaster = server0.example.com
certmaster_port = 51235
log_level = DEBUG
cert_dir = /etc/pki/certmaster
Now, back on server0 we'll need to sign client15's certificate.
[root@server0 ~]# certmaster-ca --list
client15.example.com
[root@server0 ~]# certmaster-ca --sign client15.example.com
/var/lib/certmaster/certmaster/csrs/client15.example.com.csr signed - cert located at /var/lib/certmaster/certmaster/certs/client15.example.com.cert
[root@server0 ~]# certmaster-ca --list
No certificates to sign
At this point out client has connected to our server and requested a certificate, we then signed that certificate. We can now ask the client to do something.
[root@server0 certmaster]# func client15.example.com call hardware info
{'client15.example.com': ['REMOTE_ERROR',
                          'socket.error',
                          "(113, 'No route to host')",
                          '  File "/usr/lib/python2.4/site-packages/func/overlord/client.py", line 433, in process_server\n    retval = getattr(conn, meth)(*args[:])\n   File "/usr/lib64/python2.4/xmlrpclib.py", line 1096, in __call__\n    return self.__send(self.__name, args)\n   File "/usr/lib64/python2.4/xmlrpclib.py", line 1383, in __request\n    verbose=self.__verbose\n   File "/usr/lib64/python2.4/xmlrpclib.py", line 1129, in request\n    self.send_content(h, request_body)\n   File "/usr/lib64/python2.4/xmlrpclib.py", line 1243, in send_content\n    connection.endheaders()\n   File "/usr/lib64/python2.4/httplib.py", line 804, in endheaders\n    self._send_output()\n   File "/usr/lib64/python2.4/httplib.py", line 685, in _send_output\n    self.send(msg)\n   File "/usr/lib64/python2.4/httplib.py", line 652, in send\n    self.connect()\n   File "/usr/lib/python2.4/site-packages/certmaster/SSLCommon.py", line 106, in connect\n    self.sock.connect((self.host, self.port))\n   File "", line 1, in connect\n']}=
Almost, not quite, we need to open the port on the firewall on the client so we can talk to the funcd running on client15. We'll do this with puppet, there is an iptables module available. We'll just use service and remotefile in this example (We'll also change the default policy from REJECT to DROP)
[root@server0 ~]# cd /var/lib/puppet/files/base/etc/sysconfig
[root@server0 sysconfig]# vi iptables
[root@server0 sysconfig]# cat iptables
*filter
:INPUT ACCEPT [0:0]
:FORWARD ACCEPT [0:0]
:OUTPUT ACCEPT [0:0]
:Example.com-1-INPUT - [0:0]
-A INPUT -j Example.com-1-INPUT
-A FORWARD -j Example.com-1-INPUT
-A Example.com-1-INPUT -i lo -j ACCEPT
-A Example.com-1-INPUT -p icmp --icmp-type any -j ACCEPT
-A Example.com-1-INPUT -p 50 -j ACCEPT
-A Example.com-1-INPUT -p 51 -j ACCEPT
-A Example.com-1-INPUT -p udp --dport 5353 -d 224.0.0.251 -j ACCEPT
-A Example.com-1-INPUT -p udp -m udp --dport 631 -j ACCEPT
-A Example.com-1-INPUT -p tcp -m tcp --dport 631 -j ACCEPT
-A Example.com-1-INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT
-A Example.com-1-INPUT -m state --state NEW -m tcp -p tcp --dport 51234 -j ACCEPT
-A Example.com-1-INPUT -m state --state NEW -m tcp -p tcp --dport 22 -j ACCEPT
-A Example.com-1-INPUT -j DROP
COMMIT
[root@server0 sysconfig]# cd /etc/puppet/manifests
[root@server0 manifests]# vi func.pp
[root@server0 manifests]# cat func.pp
class func {
	remotefile { "/etc/certmaster/minion.conf": mode => 644 }
	service { funcd: ensure => true, enable => true, hasrestart => true }
	remotefile { "/etc/sysconfig/iptables": mode => 644,
		notify => Service["iptables"] }
	service { iptables: ensure => true, enable => true, hasrestart => true }
}
Now, back on client15, we again could wait for the change to be propagated, but we'll just trigger it.
[root@client15 ~]# puppetd --fqdn=$HOSTNAME --test --no-splay --server=server0.example.com --onetime --verbose --factsync
info: Retrieving facts
info: Caching catalog at /var/lib/puppet/localconfig.yaml
notice: Starting catalog run
1,2d0
 # Firewall configuration written by system-config-securitylevel
 # Manual customization of this file is not recommended.
7,19c5,18
...
notice: //Node[default]/func/Service[iptables]/ensure: ensure changed 'stopped' to 'running'
notice: //Node[default]/func/Service[iptables]: Triggering 'refresh' from 2 dependencies
notice: Finished catalog run in 2.55 seconds
[root@client15 ~]# 
Back on server0, we'll try our func call again.
[root@server0 ~]# func client15.example.com call hardware info
{'client15.example.com': {'bogomips': '7187.63',
                          'cpuModel': 'Intel(R) Pentium(R) 4 CPU 3.60GHz',
                          'cpuSpeed': '3590',
                          'cpuVendor': 'GenuineIntel',
                          'defaultRunlevel': '3',
...
                          'systemSwap': '8191',
                          'systemVendor': 'Dell Inc.'}}
At this point you could configure certmaster to automatically sign keys as we did for puppet. Anything we need to do immediately, we can now do in func. If you want to use func for inventory purposes, you'll need to also download a git rpm and install that on server0. If you try to use func-inventory without git, it will work, but changes to the inventory won't be tracked.
[root@server0 ~]# func-inventory
git-core is not installed, so no change tracking is available.
use --no-git or, better, just install it.
[root@server0 ~]# func-inventory --no-git
The inventory files will be stored in /var/lib/func/inventory.