[augeas-devel] [PATCH] Add a lens for keepalived.conf and associated test

raphink at gmail.com raphink at gmail.com
Wed Sep 22 09:05:52 UTC 2010


From: =?UTF-8?q?Rapha=C3=ABl=20Pinson?= <raphink at gmail.com>

---
 lenses/keepalived.aug            |  249 ++++++++++++++++++++++++++++++++++++++
 lenses/tests/test_keepalived.aug |  232 +++++++++++++++++++++++++++++++++++
 2 files changed, 481 insertions(+), 0 deletions(-)
 create mode 100644 lenses/keepalived.aug
 create mode 100644 lenses/tests/test_keepalived.aug

diff --git a/lenses/keepalived.aug b/lenses/keepalived.aug
new file mode 100644
index 0000000..b661590
--- /dev/null
+++ b/lenses/keepalived.aug
@@ -0,0 +1,249 @@
+(*
+Module: Keepalived
+  Parses /etc/keepalived/keepalived.conf
+
+Author: Raphael Pinson <raphink at gmail.com>
+
+About: Reference
+  This lens tries to keep as close as possible to `man 5 keepalived.conf` where possible.
+
+About: License
+   This file is licenced under the LGPLv2+, like the rest of Augeas.
+
+About: Lens Usage
+   To be documented
+
+About: Configuration files
+   This lens applies to /etc/keepalived/keepalived.conf. See <filter>.
+*)
+
+
+module Keepalived =
+  autoload xfm
+
+(************************************************************************
+ * Group:                 USEFUL PRIMITIVES
+ *************************************************************************)
+
+(* Group: Comments and empty lines *)
+
+(* View: indent *)
+let indent = Util.indent
+
+(* View: eol *)
+let eol = Util.eol
+
+(* View: sep_spc *)
+let sep_spc = del /[ \t]+/ " "
+
+(* View: lbracket *)
+let lbracket = Util.del_str "{"
+
+(* View: rbracket *)
+let rbracket = Util.del_str "}"
+
+(* View: comment
+Map comments in "#comment" nodes *)
+let comment = 
+    [ indent . label "#comment" . del /[#!][ \t]*/ "# "
+        . store /([^ \t\n].*[^ \t\n]|[^ \t\n])/ . eol ]
+
+(* View: eol_comment
+And end-of-line comment
+with a space in front of # by default *)
+let eol_comment =
+    [ label "#comment" . del /[ \t]*[#!][ \t]*/ " # "
+        . store /([^ \t\n].*[^ \t\n]|[^ \t\n])/ . eol ]
+
+(* View: empty
+Map empty lines *)
+let empty   = Util.empty
+
+(* View: sto_email_addr *)
+let sto_email_addr = store /[A-Za-z0-9_\+\.-]+@[A-Za-z0-9_\.-]+/
+
+(* Variable: word *)
+let word = /[A-Za-z0-9_\.-]+/
+
+(* Variable: word_slash *)
+let word_slash = /[A-Za-z0-9_\.\/-]+/
+
+(* View: sto_word *)
+let sto_word = store word
+
+(* View: sto_num *)
+let sto_num = store /[0-9]+/
+
+(* View: sto_to_eol
+Should not be necessary once static_ipaddress_field uses fields *)
+let sto_to_eol = store /[^}!# \t\n][^}!#\n]*[^}!# \t\n]|[^}!# \t\n]/
+
+
+(* View: field *)
+let field (kw:string) (sto:lens) = [ indent . key kw . sep_spc . sto . (eol_comment|eol) ]
+
+(* View: flag
+A single word *)
+let flag (kw:regexp) = [ indent . key kw ]
+
+(* View: lens_block 
+A generic block with a title lens *)
+let lens_block (title:lens) (sto:lens) = [ indent . title . sep_spc . lbracket . eol
+                                         . (sto | empty | comment)+
+                                         . indent . rbracket . eol ]
+
+(* View: block
+A simple block with just a block title *)
+let block (kw:string) (sto:lens) = lens_block (key kw) sto
+
+(* View: named_block 
+A block with a block title and name *)
+let named_block (kw:string) (sto:lens) = lens_block (key kw . sep_spc . sto_word) sto
+
+(* View: named_block_arg_title
+A title lens for named_block_arg *)
+let named_block_arg_title (kw:string) (name:string) (arg:string) = 
+                            key kw . sep_spc
+                          . [ label name . sto_word ]
+                          . sep_spc
+                          . [ label arg . sto_word ]
+
+(* View: named_block_arg
+A block with a block title, a name and an argument *)
+let named_block_arg (kw:string) (name:string) (arg:string) (sto:lens) = 
+                           lens_block (named_block_arg_title kw name arg) sto
+
+
+(************************************************************************
+ * Group:                 GLOBAL CONFIGURATION
+ *************************************************************************)
+
+(* View: email
+A simple email address entry *)
+let email = [ indent . label "email" . sto_email_addr . (eol_comment|eol) ]
+
+(* View: global_defs_field
+Possible fields in the global_defs block *)
+let global_defs_field = block "notification_email" email
+                      | field "notification_email_from" sto_email_addr
+                      | field "smtp_server" sto_word
+                      | field "smtp_connect_timeout" sto_num
+                      | field "lvs_id" sto_word
+                      | field "router_id" sto_word
+
+(* View: global_defs
+A global_defs block *)
+let global_defs = block "global_defs" global_defs_field
+
+(* View: prefixlen
+A prefix for IP addresses *)
+let prefixlen = [ label "prefixlen" . Util.del_str "/" . sto_num ]
+
+(* View: ipdev
+A device for IP addresses *)
+let ipdev = [ key "dev" . sep_spc . sto_word ]
+
+(* View: static_ipaddress_field
+The whole string is fed to ip addr add.
+You can truncate the string anywhere you like and let ip addr add use defaults for the rest of the string.
+To be refined with fields according to `ip addr help`.
+*)
+let static_ipaddress_field = [ indent . label "ipaddr"
+                             . store /[0-9\.]+/
+                             . prefixlen?
+                             . (sep_spc . ipdev)?
+                             . (eol|eol_comment) ]
+
+(* View: static_routes_field
+src $SRC_IP to $DST_IP dev $SRC_DEVICE
+*)
+let static_routes_field = [ indent . label "route"
+                          . [ key "src" . sto_word ] . sep_spc
+                          . [ key "to"  . sto_word ] . sep_spc
+                          . [ key "dev" . sto_word ] . (eol|eol_comment) ]
+
+(* View: static_routes *)
+let static_routes = block "static_ipaddress" static_ipaddress_field
+                  | block "static_routes" static_routes_field
+
+
+(* View: global_conf 
+A global configuration entry *)
+let global_conf = global_defs | static_routes
+
+
+(************************************************************************
+ * Group:                 VRRP CONFIGURATION
+ *************************************************************************)
+
+(*View: vrrp_sync_group_field *)
+let vrrp_sync_group_field = block "group" [ indent . key word . (eol|eol_comment) ]
+
+(* View: vrrp_sync_group *)
+let vrrp_sync_group = named_block "vrrp_sync_group" vrrp_sync_group_field
+
+(* View: vrrp_instance_field *)
+let vrrp_instance_field = field "state" sto_word
+                        | field "interface" sto_word
+                        | field "lvs_sync_daemon_interface" sto_word
+                        | field "virtual_router_id" sto_num
+                        | field "priority" sto_num
+                        | field "advert_int" sto_num
+                        | flag "smtp_alert"
+                        | flag "nopreempt"
+                        | block "authentication" (
+                                field "auth_type" sto_word
+                              | field "auth_pass" sto_word
+                              )
+                        | block "virtual_ipaddress" static_ipaddress_field
+
+(* View: vrrp_instance *)
+let vrrp_instance = named_block "vrrp_instance" vrrp_instance_field
+
+
+(* View: vrrpd_conf
+contains subblocks of VRRP synchronization group(s) and VRRP instance(s) *)
+let vrrpd_conf = vrrp_sync_group | vrrp_instance
+
+
+(************************************************************************
+ * Group:                 LVS CONFIGURATION
+ *************************************************************************)
+
+(* View: virtual_server_group *)
+
+(* View: tcp_check_field *)
+let tcp_check_field = field "connect_timeout" sto_num
+                    | field "connect_port" sto_num
+
+(* View: real_server_field *)
+let real_server_field = field "weight" sto_num
+                      | block "TCP_CHECK" tcp_check_field
+
+(* View: virtual_server_field *)
+let virtual_server_field = field "delay_loop" sto_num
+                         | field "lb_algo" sto_word
+                         | field "lb_kind" sto_word
+                         | field "nat_mask" sto_word
+                         | field "protocol" sto_word
+                         | named_block_arg "real_server" "ip" "port" real_server_field
+
+(* View: virtual_server *)
+let virtual_server = named_block_arg "virtual_server" "ip" "port" virtual_server_field
+
+(* View: lvs_conf
+contains subblocks of Virtual server group(s) and Virtual server(s) *)
+let lvs_conf = virtual_server
+
+
+(* View: lns
+     The keepalived lens
+*)
+let lns = ( empty | comment | global_conf | vrrpd_conf | lvs_conf )*
+
+(* Variable: filter *)
+let filter = incl "/etc/keepalived/keepalived.conf"
+    . Util.stdexcl
+
+let xfm = transform lns filter
+
diff --git a/lenses/tests/test_keepalived.aug b/lenses/tests/test_keepalived.aug
new file mode 100644
index 0000000..e2f208a
--- /dev/null
+++ b/lenses/tests/test_keepalived.aug
@@ -0,0 +1,232 @@
+(* Test for keepalived lens *)
+
+module Test_keepalived =
+
+   let conf = "! This is a comment 
+! Configuration File for keepalived 
+
+global_defs { 
+   ! this is who emails will go to on alerts 
+   notification_email { 
+        admins at example.com 
+    fakepager at example.com 
+    ! add a few more email addresses here if you would like 
+   } 
+   notification_email_from admins at example.com 
+
+   smtp_server 127.0.0.1  ! I use the local machine to relay mail 
+   smtp_connect_timeout 30 
+
+   ! each load balancer should have a different ID 
+   ! this will be used in SMTP alerts, so you should make 
+   ! each router easily identifiable 
+   lvs_id LVS_EXAMPLE_01 
+} 
+
+vrrp_sync_group VG1 { 
+   group { 
+      inside_network  # name of vrrp_instance (below)
+      outside_network # One for each moveable IP.  
+   } 
+} 
+
+vrrp_instance VI_1 { 
+        state MASTER 
+        interface eth0 
+     
+        lvs_sync_daemon_interface eth0 
+
+    ! each virtual router id must be unique per instance name! 
+        virtual_router_id 51 
+
+    ! MASTER and BACKUP state are determined by the priority 
+    ! even if you specify MASTER as the state, the state will 
+    ! be voted on by priority (so if your state is MASTER but your 
+    ! priority is lower than the router with BACKUP, you will lose 
+    ! the MASTER state) 
+    ! I make it a habit to set priorities at least 50 points apart 
+    ! note that a lower number is lesser priority - lower gets less vote 
+        priority 150 
+
+    ! how often should we vote, in seconds? 
+        advert_int 1 
+
+    ! send an alert when this instance changes state from MASTER to BACKUP 
+        smtp_alert 
+
+    ! this authentication is for syncing between failover servers 
+    ! keepalived supports PASS, which is simple password 
+    ! authentication 
+    ! or AH, which is the IPSec authentication header. 
+    ! I don't use AH 
+    ! yet as many people have reported problems with it 
+        authentication { 
+                auth_type PASS 
+                auth_pass example 
+        } 
+
+    ! these are the IP addresses that keepalived will setup on this 
+    ! machine. Later in the config we will specify which real 
+        ! servers  are behind these IPs 
+    ! without this block, keepalived will not setup and takedown the 
+    ! any IP addresses 
+     
+        virtual_ipaddress { 
+                192.168.1.11 
+                10.234.66.146/32 dev vlan933 # parse it well
+        ! and more if you want them 
+        } 
+} 
+
+virtual_server 192.168.1.11 22 { 
+    delay_loop 6 
+
+    ! use round-robin as a load balancing algorithm 
+    lb_algo rr 
+
+    ! we are doing NAT 
+    lb_kind NAT 
+    nat_mask 255.255.255.0 
+
+    protocol TCP 
+
+    ! there can be as many real_server blocks as you need 
+
+    real_server 10.20.40.10 22 { 
+
+    ! if we used weighted round-robin or a similar lb algo, 
+    ! we include the weight of this server 
+
+        weight 1 
+
+    ! here is a health checker for this server. 
+    ! we could use a custom script here (see the keepalived docs) 
+    ! but we will just make sure we can do a vanilla tcp connect() 
+    ! on port 22 
+    ! if it fails, we will pull this realserver out of the pool 
+    ! and send email about the removal 
+        TCP_CHECK { 
+                connect_timeout 3 
+        connect_port 22 
+        } 
+    } 
+} 
+
+! that's all
+"
+
+
+   test Keepalived.lns get conf =
+     { "#comment" = "This is a comment" }
+     { "#comment" = "Configuration File for keepalived" }
+     {}
+     { "global_defs"
+       { "#comment" = "this is who emails will go to on alerts" }
+       { "notification_email"
+            { "email" = "admins at example.com" }
+            { "email" = "fakepager at example.com" }
+            { "#comment" = "add a few more email addresses here if you would like" } }
+       { "notification_email_from" = "admins at example.com" }
+       { }
+       { "smtp_server" = "127.0.0.1"
+         { "#comment" = "I use the local machine to relay mail" } }
+       { "smtp_connect_timeout" = "30" }
+       {}
+       { "#comment" = "each load balancer should have a different ID" }
+       { "#comment" = "this will be used in SMTP alerts, so you should make" }
+       { "#comment" = "each router easily identifiable" }
+       { "lvs_id" = "LVS_EXAMPLE_01" } }
+     {}
+     { "vrrp_sync_group" = "VG1"
+       { "group"
+         { "inside_network"
+           { "#comment" = "name of vrrp_instance (below)" } }
+         { "outside_network"
+           { "#comment" = "One for each moveable IP." } } } }
+     {}
+     { "vrrp_instance" = "VI_1"
+       { "state" = "MASTER" }
+       { "interface" = "eth0" }
+       { }
+       { "lvs_sync_daemon_interface" = "eth0" }
+       { }
+       { "#comment" = "each virtual router id must be unique per instance name!" }
+       { "virtual_router_id" = "51" }
+       { }
+       { "#comment" = "MASTER and BACKUP state are determined by the priority" }
+       { "#comment" = "even if you specify MASTER as the state, the state will" }
+       { "#comment" = "be voted on by priority (so if your state is MASTER but your" }
+       { "#comment" = "priority is lower than the router with BACKUP, you will lose" }
+       { "#comment" = "the MASTER state)" }
+       { "#comment" = "I make it a habit to set priorities at least 50 points apart" }
+       { "#comment" = "note that a lower number is lesser priority - lower gets less vote" }
+       { "priority" = "150" }
+       { }
+       { "#comment" = "how often should we vote, in seconds?" }
+       { "advert_int" = "1" }
+       { }
+       { "#comment" = "send an alert when this instance changes state from MASTER to BACKUP" }
+       { "smtp_alert" }
+       { }
+       { }
+       { "#comment" = "this authentication is for syncing between failover servers" }
+       { "#comment" = "keepalived supports PASS, which is simple password" }
+       { "#comment" = "authentication" }
+       { "#comment" = "or AH, which is the IPSec authentication header." }
+       { "#comment" = "I don't use AH" }
+       { "#comment" = "yet as many people have reported problems with it" }
+       { "authentication"
+         { "auth_type" = "PASS" }
+         { "auth_pass" = "example" } }
+       { }
+       { "#comment" = "these are the IP addresses that keepalived will setup on this" }
+       { "#comment" = "machine. Later in the config we will specify which real" }
+       { "#comment" = "servers  are behind these IPs" }
+       { "#comment" = "without this block, keepalived will not setup and takedown the" }
+       { "#comment" = "any IP addresses" }
+       { }
+       { "virtual_ipaddress"
+         { "ipaddr" = "192.168.1.11" }
+         { "ipaddr" = "10.234.66.146"
+           { "prefixlen" = "32" }
+           { "dev" = "vlan933" }
+           { "#comment" = "parse it well" } }
+         { "#comment" = "and more if you want them" } } }
+       { }
+       { "virtual_server"
+         { "ip" = "192.168.1.11" }
+         { "port" = "22" }
+         { "delay_loop" = "6" }
+         { }
+         { "#comment" = "use round-robin as a load balancing algorithm" }
+         { "lb_algo" = "rr" }
+         { }
+         { "#comment" = "we are doing NAT" }
+         { "lb_kind" = "NAT" }
+         { "nat_mask" = "255.255.255.0" }
+         { }
+         { "protocol" = "TCP" }
+         { }
+         { "#comment" = "there can be as many real_server blocks as you need" }
+         { }
+         { "real_server"
+           { "ip" = "10.20.40.10" }
+           { "port" = "22" }
+           { }
+           { "#comment" = "if we used weighted round-robin or a similar lb algo," }
+           { "#comment" = "we include the weight of this server" }
+           { }
+           { "weight" = "1" }
+           { }
+           { "#comment" = "here is a health checker for this server." }
+           { "#comment" = "we could use a custom script here (see the keepalived docs)" }
+           { "#comment" = "but we will just make sure we can do a vanilla tcp connect()" }
+           { "#comment" = "on port 22" }
+           { "#comment" = "if it fails, we will pull this realserver out of the pool" }
+           { "#comment" = "and send email about the removal" }
+           { "TCP_CHECK"
+             { "connect_timeout" = "3" }
+             { "connect_port" = "22" } } } }
+       { }
+       { "#comment" = "that's all" }
+
-- 
1.7.0.4




More information about the augeas-devel mailing list