start API for aliases
authorDietmar Maurer <dietmar@proxmox.com>
Tue, 22 Apr 2014 09:45:52 +0000 (11:45 +0200)
committerDietmar Maurer <dietmar@proxmox.com>
Tue, 22 Apr 2014 09:45:52 +0000 (11:45 +0200)
Allow comments for aliases.

src/PVE/API2/Firewall/Aliases.pm [new file with mode: 0644]
src/PVE/API2/Firewall/Cluster.pm
src/PVE/API2/Firewall/Makefile
src/PVE/Firewall.pm

diff --git a/src/PVE/API2/Firewall/Aliases.pm b/src/PVE/API2/Firewall/Aliases.pm
new file mode 100644 (file)
index 0000000..86393e6
--- /dev/null
@@ -0,0 +1,296 @@
+package PVE::API2::Firewall::AliasesBase;
+
+use strict;
+use warnings;
+use PVE::Exception qw(raise raise_param_exc);
+use PVE::JSONSchema qw(get_standard_option);
+
+use PVE::Firewall;
+
+use base qw(PVE::RESTHandler);
+
+my $api_properties = { 
+    cidr => {
+       description => "Network/IP specification in CIDR format.",
+       type => 'string', format => 'IPv4orCIDR',
+    },
+    name => get_standard_option('pve-fw-alias'),
+    comment => {
+       type => 'string',
+       optional => 1,
+    }
+};
+
+sub load_config {
+    my ($class, $param) = @_;
+
+    die "implement this in subclass";
+
+    #return ($fw_conf, $rules);
+}
+
+sub save_aliases {
+    my ($class, $param, $fw_conf, $aliases) = @_;
+
+    die "implement this in subclass";
+}
+
+my $additional_param_hash = {};
+
+sub additional_parameters {
+    my ($class, $new_value) = @_;
+
+    if (defined($new_value)) {
+       $additional_param_hash->{$class} = $new_value;
+    }
+
+    # return a copy
+    my $copy = {};
+    my $org = $additional_param_hash->{$class} || {};
+    foreach my $p (keys %$org) { $copy->{$p} = $org->{$p}; }
+    return $copy;
+}
+
+my $aliases_to_list = sub {
+    my ($aliases) = @_;
+
+    my $list = [];
+    foreach my $k (sort keys %$aliases) {
+       push @$list, $aliases->{$k};
+    }
+    return $list;
+};
+
+sub register_get_aliases {
+    my ($class) = @_;
+
+    my $properties = $class->additional_parameters();
+
+    $class->register_method({
+       name => 'get_aliases',
+       path => '',
+       method => 'GET',
+       description => "List aliases",
+       parameters => {
+           additionalProperties => 0,
+           properties => $properties,
+       },
+       returns => {
+           type => 'array',
+           items => {
+               type => "object",
+               properties => {
+                   name => { type => 'string' },
+                   cidr => { type => 'string' },
+                   comment => {
+                       type => 'string',
+                       optional => 1,
+                   },
+                   digest => get_standard_option('pve-config-digest', { optional => 0} ),      
+               },
+           },
+           links => [ { rel => 'child', href => "{name}" } ],
+       },
+       code => sub {
+           my ($param) = @_;
+
+           my ($fw_conf, $aliases) = $class->load_config($param);
+
+           my $list = &$aliases_to_list($aliases);
+
+           return PVE::Firewall::copy_list_with_digest($list);
+       }});
+}
+
+sub register_create_alias {
+    my ($class) = @_;
+
+    my $properties = $class->additional_parameters();
+
+    $properties->{name} = $api_properties->{name};
+    $properties->{cidr} = $api_properties->{cidr};
+    $properties->{comment} = $api_properties->{comment};
+
+    $class->register_method({
+       name => 'create_alias',
+       path => '',
+       method => 'POST',
+       description => "Create IP or Network Alias.",
+       protected => 1,
+       parameters => {
+           additionalProperties => 0,
+           properties => $properties,
+       },
+       returns => { type => "null" },
+       code => sub {
+           my ($param) = @_;
+
+           my ($fw_conf, $aliases) = $class->load_config($param);
+
+           my $name = lc($param->{name});
+           
+           raise_param_exc({ name => "alias '$param->{name}' already exists" }) 
+               if defined($aliases->{$name});
+           
+           my $data = { name => $param->{name}, cidr => $param->{cidr} };
+           $data->{comment} = $param->{comment} if $param->{comment};
+
+           $aliases->{$name} = $data;
+
+           $class->save_aliases($param, $fw_conf, $aliases);
+
+           return undef;
+       }});
+}
+
+sub register_read_alias {
+    my ($class) = @_;
+
+    my $properties = $class->additional_parameters();
+
+    $properties->{name} = $api_properties->{name};
+    $properties->{cidr} = $api_properties->{cidr};
+    
+    $class->register_method({
+       name => 'read_alias',
+       path => '{name}',
+       method => 'GET',
+       description => "Read alias.",
+       parameters => {
+           additionalProperties => 0,
+           properties => $properties,
+       },
+       returns => { type => "object" },
+       code => sub {
+           my ($param) = @_;
+
+           my ($fw_conf, $aliases) = $class->load_config($param);
+
+           my $name = lc($param->{name});
+
+           raise_param_exc({ name => "no such alias" })
+               if !defined($aliases->{$name});
+
+           return $aliases->{$name};
+       }});
+}
+
+sub register_update_alias {
+    my ($class) = @_;
+
+    my $properties = $class->additional_parameters();
+
+    $properties->{name} = $api_properties->{name};
+    $properties->{cidr} = $api_properties->{cidr};
+    $properties->{comment} = $api_properties->{comment};
+    $properties->{digest} = get_standard_option('pve-config-digest');
+
+    $class->register_method({
+       name => 'update_alias',
+       path => '{name}',
+       method => 'PUT',
+       description => "Update IP or Network alias.",
+       protected => 1,
+       parameters => {
+           additionalProperties => 0,
+           properties => $properties,
+       },
+       returns => { type => "null" },
+       code => sub {
+           my ($param) = @_;
+
+           my ($fw_conf, $aliases) = $class->load_config($param);
+
+           my $list = &$aliases_to_list($aliases);
+
+           my (undef, $digest) = PVE::Firewall::copy_list_with_digest($list);
+
+           PVE::Tools::assert_if_modified($digest, $param->{digest});
+
+           my $name = lc($param->{name});
+
+           raise_param_exc({ name => "no such alias" }) if !$aliases->{$name};
+
+           my $data = { name => $param->{name}, cidr => $param->{cidr} };
+           $data->{comment} = $param->{comment} if $param->{comment};
+
+           $aliases->{$name} = $data;
+
+           $class->save_aliases($param, $fw_conf, $aliases);
+       }});
+}
+
+sub register_delete_alias {
+    my ($class) = @_;
+
+    my $properties = $class->additional_parameters();
+
+    $properties->{name} = $api_properties->{name};
+    $properties->{cidr} = $api_properties->{cidr};
+    $properties->{digest} = get_standard_option('pve-config-digest');
+
+    $class->register_method({
+       name => 'remove_alias',
+       path => '{name}',
+       method => 'DELETE',
+       description => "Remove IP or Network alias.",
+       protected => 1,
+       parameters => {
+           additionalProperties => 0,
+           properties => $properties,
+       },
+       returns => { type => "null" },
+       code => sub {
+           my ($param) = @_;
+
+           my ($fw_conf, $aliases) = $class->load_config($param);
+
+           my $list = &$aliases_to_list($aliases);
+           my (undef, $digest) = PVE::Firewall::copy_list_with_digest($list);
+           PVE::Tools::assert_if_modified($digest, $param->{digest});
+
+           my $name = lc($param->{name});
+           delete $aliases->{$name};
+
+           $class->save_aliases($param, $fw_conf, $aliases);
+           
+           return undef;
+       }});
+}
+
+sub register_handlers {
+    my ($class) = @_;
+
+    $class->register_get_aliases();
+    $class->register_create_alias();
+    $class->register_read_alias();
+    $class->register_update_alias();
+    $class->register_delete_alias();
+}
+
+package PVE::API2::Firewall::ClusterAliases;
+
+use strict;
+use warnings;
+
+use base qw(PVE::API2::Firewall::AliasesBase);
+
+sub load_config {
+    my ($class, $param) = @_;
+
+    my $fw_conf = PVE::Firewall::load_clusterfw_conf();
+    my $aliases = $fw_conf->{aliases};
+
+    return ($fw_conf, $aliases);
+}
+
+sub save_aliases {
+    my ($class, $param, $fw_conf, $aliases) = @_;
+
+    $fw_conf->{aliases} = $aliases;
+    PVE::Firewall::save_clusterfw_conf($fw_conf);
+}
+
+__PACKAGE__->register_handlers();
+
+1;
index d104c34..5f6d77d 100644 (file)
@@ -6,6 +6,7 @@ use PVE::Exception qw(raise raise_param_exc raise_perm_exc);
 use PVE::JSONSchema qw(get_standard_option);
 
 use PVE::Firewall;
+use PVE::API2::Firewall::Aliases;
 use PVE::API2::Firewall::Rules;
 use PVE::API2::Firewall::Groups;
 use PVE::API2::Firewall::IPSet;
@@ -31,6 +32,12 @@ __PACKAGE__->register_method ({
     path => 'ipset',
 });
 
+__PACKAGE__->register_method ({
+    subclass => "PVE::API2::Firewall::ClusterAliases",  
+    path => 'aliases',
+});
+
+
 __PACKAGE__->register_method({
     name => 'index',
     path => '',
@@ -52,6 +59,7 @@ __PACKAGE__->register_method({
        my ($param) = @_;
 
        my $result = [
+           { name => 'aliases' },
            { name => 'rules' },
            { name => 'options' },
            { name => 'groups' },
index acbf9a2..7c5988b 100644 (file)
@@ -1,4 +1,5 @@
 LIB_SOURCES=                   \
+       Aliases.pm              \
        IPSet.pm                \
        Rules.pm                \
        Cluster.pm              \
index 7ac8a4a..8d25326 100644 (file)
@@ -56,6 +56,14 @@ PVE::JSONSchema::register_standard_option('ipset-name', {
     maxLength => 20,                     
 });
 
+PVE::JSONSchema::register_standard_option('pve-fw-alias', {
+    description => "Alias name.",
+    type => 'string',
+    pattern => '[A-Za-z][A-Za-z0-9\-\_]+',
+    minLength => 2,
+    maxLength => 20,                     
+});
+
 PVE::JSONSchema::register_standard_option('pve-fw-loglevel' => {
     description => "Log level.",
     type => 'string', 
@@ -1214,9 +1222,10 @@ sub ruleset_generate_cmdstr {
                die "invalid security group name '$source'\n";
            }
        } elsif ($source =~ m/^${ip_alias_pattern}$/){
-           die "no such alias $source\n" if !$cluster_conf->{aliases}->{$source};
-           push @cmd, "-s $cluster_conf->{aliases}->{$source}";
-
+           my $alias = lc($source);
+           my $e = $cluster_conf->{aliases}->{$alias};
+           die "no such alias $source\n" if !$e;
+           push @cmd, "-s $e->{cidr}";
         } elsif ($source =~ m/\-/){
            push @cmd, "-m iprange --src-range $source";
 
@@ -1234,9 +1243,10 @@ sub ruleset_generate_cmdstr {
                die "invalid security group name '$dest'\n";
            }
        } elsif ($dest =~ m/^${ip_alias_pattern}$/){
-           die "no such alias $dest" if !$cluster_conf->{aliases}->{$dest};
-           push @cmd, "-d $cluster_conf->{aliases}->{$dest}";
-
+           my $alias = lc($source);
+           my $e = $cluster_conf->{aliases}->{$alias};
+           die "no such alias $dest" if !$e;
+           push @cmd, "-d $e->{cidr}";
         } elsif ($dest =~ m/^(\d+)\.(\d+).(\d+).(\d+)\-(\d+)\.(\d+).(\d+).(\d+)$/){
            push @cmd, "-m iprange --dst-range $dest";
 
@@ -1942,20 +1952,22 @@ sub parse_clusterfw_option {
 sub parse_clusterfw_alias {
     my ($line) = @_;
 
-    my ($opt, $value);
+    # we can add single line comments to the end of the line
+    my $comment = decode('utf8', $1) if $line =~ s/\s*#\s*(.*?)\s*$//;
+
     if ($line =~ m/^(\S+)\s(\S+)$/) {
-       $opt = lc($1);
-       if($2){
-           $2 =~ s|/32$||;
-           pve_verify_ipv4_or_cidr($2) if $2;
-           $value = $2;
-       }
-    } else {
-       chomp $line;
-       die "can't parse option '$line'\n";
+       my ($name, $cidr) = ($1, $2);
+       $cidr =~ s|/32$||;
+       pve_verify_ipv4_or_cidr($cidr);
+       my $data = {
+           name => $name,
+           cidr => $cidr,
+       };
+       $data->{comment} = $comment  if $comment;
+       return $data;
     }
 
-    return ($opt, $value);
+    return undef;
 }
 
 sub parse_vm_fw_rules {
@@ -2063,6 +2075,7 @@ sub parse_cluster_fw_rules {
     my $res = { 
        rules => [], 
        options => {}, 
+       aliases => {}, 
        groups => {}, 
        group_comments => {}, 
        ipset => {} ,
@@ -2124,8 +2137,8 @@ sub parse_cluster_fw_rules {
            warn "$prefix: $@" if $@;
        } elsif ($section eq 'aliases') {
            eval {
-               my ($opt, $value) = parse_clusterfw_alias($line);
-               $res->{aliases}->{$opt} = $value;
+               my $data = parse_clusterfw_alias($line);
+               $res->{aliases}->{lc($data->{name})} = $data;
            };
            warn "$prefix: $@" if $@;
        } elsif ($section eq 'rules') {
@@ -2306,7 +2319,11 @@ my $format_aliases = sub {
 
     $raw .= "[ALIASES]\n\n";
     foreach my $k (keys %$aliases) {
-       $raw .= "$k $aliases->{$k}\n";
+       my $e = $aliases->{$k};
+       $raw .= "$e->{name} $e->{cidr}";
+       $raw .= " # " . encode('utf8', $e->{comment})
+           if $e->{comment} && $e->{comment} !~ m/^\s*$/;
+       $raw .= "\n";
     }
     $raw .= "\n";
 
@@ -2442,10 +2459,11 @@ sub generate_ipset {
     foreach my $entry (@$options) {
        my $cidr = $entry->{cidr};
        if ($cidr =~ m/^${ip_alias_pattern}$/) {
-           if ($aliases->{$cidr}) {
-               $entry->{cidr} = $aliases->{$cidr};
+           my $alias = lc($cidr);
+           if ($aliases->{$alias}) {
+               $entry->{cidr} = $aliases->{$alias}->{cidr};
            } else {
-               warn "no such alias '$cidr'\n" if !$aliases->{$cidr};
+               warn "no such alias '$cidr'\n" if !$aliases->{$alias};
            }
        }
        $nethash->{$entry->{cidr}} = $entry;