=head1 NAME

Brocade::BSC::Node::NC::Vrouter::VR5600

=head1 DESCRIPTION

The I<::Vrouter::VR5600> object models a Vyatta 5600 Virtual router
controlled via netconf by a I<Brocade::BSC> instance.

=cut

use strict;
use warnings;

# Package ==============================================================
# DataplaneInterfaceFirewall
#
#
# ======================================================================
package Brocade::BSC::Node::NC::Vrouter::VR5600::DataplaneInterfaceFirewall;

# Constructor ==========================================================
# Parameters: interface name
# Returns   :
#
sub new {
    my $class = shift;
    my $tagnode = shift;

    my $self = {
        tagnode => $tagnode,
        firewall => {
            inlist  => [],
            outlist => []
        }
    };
    bless ($self, $class);
}

# Method ===============================================================
#             add_in_item : append firewall rule to inbound rules list
# Parameters: firewall rule name
# Returns   :
#
sub add_in_item {
    my ($self, $item) = @_;

    push @{$self->{firewall}->{inlist}}, $item;
}
# Method ===============================================================
#             add_out_item : append firewall rule to outbound rules list
# Parameters: firewall rule name
# Returns   :
#
sub add_out_item {
    my ($self, $item) = @_;

    push @{$self->{firewall}->{outlist}}, $item;
}

# Method ===============================================================
#             get_url_extenstion: provide suffix for configuring *this*
#                                 firewall; must be appended to config urlpath
# Parameters: none
# Returns   : url suffix
#
sub get_url_extension {
    my $self = shift;

    return "vyatta-interfaces:interfaces/vyatta-interfaces-dataplane:"
        . "dataplane/$self->{tagnode}";
}

# Method ===============================================================
#             get_payload:
# Parameters: none
# Returns   : self as JSON formatted for BSC REST call
#
sub get_payload {
    my $self = shift;

    my $json = new JSON->canonical->allow_blessed->convert_blessed;
    my $payload = '{"vyatta-interfaces-dataplane:dataplane":'
        . $json->encode($self)
        . '}';
    $payload =~ s/firewall/vyatta-security-firewall:firewall/g;
    $payload =~ s/inlist/in/g;
    $payload =~ s/outlist/out/g;

    return $payload;
}



# Package ==============================================================
# Brocade::BSC::Node::NC::Vrouter::5600
#    model and interact with Vyatta Virtual Router 5600 via BSC
#
# ======================================================================

package Brocade::BSC::Node::NC::Vrouter::VR5600;

use base qw(Brocade::BSC::Node::NC);
use HTTP::Status qw(:constants :is status_message);
use JSON -convert_blessed_universally;
use Brocade::BSC;
use Brocade::BSC::Status qw(:constants);

=head1 METHODS

=over 4

=cut

# Method ===============================================================
#
=item B<get_schemas>

  # Returns   : array ref - YANG schemas supported by node

=cut ===================================================================
sub get_schemas {
    my $self = shift;

    return $self->{ctrl}->get_schemas($self->{name});
}

# Method ===============================================================
#
=item B<get_schema>

  # Returns   : requested YANG schema as formatted JSON

=cut ===================================================================
sub get_schema {
    my $self = shift;
    my ($yangId, $yangVersion) = @_;

    return $self->{ctrl}->get_schema($self->{name}, $yangId, $yangVersion);
}

# Method ===============================================================
#
=item B<get_cfg>

  # Returns   : BSC::Status
  #           : hash ref - VR5600 node configuration

=cut ===================================================================
sub get_cfg {
    my $self = shift;
    my $status = new Brocade::BSC::Status;
    my $config = undef;

    my $url = $self->{ctrl}->get_ext_mount_config_urlpath($self->{name});
    my $resp = $self->ctrl_req('GET', $url);
    if ($resp->code == HTTP_OK) {
        $config = decode_json($resp->content);
        $status->code($BSC_OK);
    }
    else {
        $status->http_err($resp);
    }
    return ($status, $config);
}

# Method ===============================================================
#
=item B<get_firewalls_cfg>

  # Returns   : BSC::Status
  #           : firewall configuration of VR5600 as a JSON string

=cut ===================================================================
sub get_firewalls_cfg {
    my $self = shift;
    my $status = new Brocade::BSC::Status;
    my $config = undef;

    my $url = $self->{ctrl}->get_ext_mount_config_urlpath($self->{name});
    $url .= "vyatta-security:security/vyatta-security-firewall:firewall";
    my $resp = $self->ctrl_req('GET', $url);
    if ($resp->code == HTTP_OK) {
        $config = $resp->content;
        $status->code($BSC_OK);
    }
    elsif ($resp->code == HTTP_NOT_FOUND) {
	$status->code($BSC_DATA_NOT_FOUND)
    }
    else {
        $status->http_err($resp);
    }
    return ($status, $config);
}

# Method ===============================================================
#
=item B<get_firewall_instance_cfg>

  # Parameters: name of firewall instance
  # Returns   : BSC::Status
  #           : specified ruleset from VR5600 firewall configuration
  #               as JSON string

=cut ===================================================================
sub get_firewall_instance_cfg {
    my $self = shift;
    my $instance = shift;
    my $status = new Brocade::BSC::Status;
    my $config = undef;

    my $url = $self->{ctrl}->get_ext_mount_config_urlpath($self->{name});
    $url .= "vyatta-security:security/vyatta-security-firewall:firewall/name/"
        . $instance;
    my $resp = $self->ctrl_req('GET', $url);
    if ($resp->code == HTTP_OK) {
        $config = $resp->content;
        $status->code($BSC_OK);
    }
    else {
        $status->http_err($resp);
    }
    return ($status, $config);
}

# Method ===============================================================
#
=item B<create_firewall_instance>

Create empty firewall instance on VR5600

  # Returns   : BSC::Status - success of operation

=cut ===================================================================
sub create_firewall_instance {
    my $self = shift;
    my $fwInstance = shift;
    my $status = new Brocade::BSC::Status($BSC_OK);

    my $urlpath = $self->{ctrl}->get_ext_mount_config_urlpath($self->{name});
    my %headers = ('content-type'=>'application/yang.data+json');
    my $payload = $fwInstance->get_payload();

    my $resp = $self->ctrl_req('POST', $urlpath, $payload, \%headers);
    $resp->is_success or $status->http_err($resp);

    return $status;
}

# Method ===============================================================
# 
# Parameters: 
# Returns   : 
#
sub add_firewall_instance_rule {
    die "XXX";
}

# Method ===============================================================
# 
# Parameters: 
# Returns   : 
#
sub update_firewall_instance_rule {
    die "XXX";
}

# Method ===============================================================
#
=item B<delete_firewall_instance>

  # Parameters: name of instance to delete
  # Returns   : BSC::Status - success of operation

=cut ===================================================================
sub delete_firewall_instance {
    my $self = shift;
    my $fwInstance = shift;
    my $status = new Brocade::BSC::Status;

    my $urlpath = $self->{ctrl}->get_ext_mount_config_urlpath($self->{name})
        . $fwInstance->get_url_extension()
        . "/name/";
    my @rules = $fwInstance->get_rules();

    foreach my $rule (@rules) {
        my $rule_url = $urlpath . $rule->get_name();
        my $resp = $self->ctrl_req('DELETE', $rule_url);
        if ($resp->code != HTTP_OK) {
            $status->http_err($resp);
            last;
        }
        else {
            $status->code($BSC_OK);
        }
    }
    return $status;
}

# Method ===============================================================
#
=item B<set_dataplane_interface_firewall>

  # Parameters: ifName (required) - dataplane interface to which to apply
  #                                   firewall rules
  #           : inFw              - name of firewall instance for inbound traffic
  #           : outFw             - name of firewall instance for outbound traffic
  # Returns   : BSC::Status       - success of operation

=cut ===================================================================
sub set_dataplane_interface_firewall {
    my ($self, %params) = @_;
    my $status = new Brocade::BSC::Status($BSC_OK);

    my %headers = ('content-type' => 'application/yang.data+json');
    my $urlpath = $self->{ctrl}->get_ext_mount_config_urlpath($self->{name});

    $params{ifName} or die "missing req'd parameter \$ifName";
    my $fw = new Brocade::BSC::Node::NC::Vrouter::VR5600::DataplaneInterfaceFirewall($params{ifName});

    $params{inFw}  and $fw->add_in_item($params{inFw});
    $params{outFw} and $fw->add_out_item($params{outFw});

    my $payload = $fw->get_payload();
    $urlpath .= $fw->get_url_extension();

    my $resp = $self->ctrl_req('PUT', $urlpath, $payload, \%headers);
    $resp->code == HTTP_OK or $status->http_err($resp);
    return $status;
}

# Method ===============================================================
#
=item B<delete_dataplane_interface_firewall>

  # Parameters: ifName (required) - interface from which to clear firewall rules
  # Returns   : BSC::Status       - success of operation

=cut ===================================================================
sub delete_dataplane_interface_firewall {
    my ($self, $ifName) = @_;

    my $status = new Brocade::BSC::Status($BSC_OK);

    my $urlpath = $self->{ctrl}->get_ext_mount_config_urlpath($self->{name})
        . "vyatta-interfaces:interfaces/vyatta-interfaces-dataplane:dataplane"
        . "/$ifName/vyatta-security-firewall:firewall/";
    my $resp = $self->ctrl_req('DELETE', $urlpath);
    $resp->code == HTTP_OK or $status->http_err($resp);
    return $status;
}

# Method ===============================================================
#
=item B<get_interfaces_list>

  # Returns   : BSC::Status
  #           : array ref - interface names

=cut ===================================================================
sub get_interfaces_list {
    my $self = shift;
    my $status = new Brocade::BSC::Status;
    my $ifcfg = undef;
    my @iflist = ();

    ($status, $ifcfg) = $self->get_interfaces_cfg();
    if ($status->ok) {
        while ($ifcfg =~ m/"tagnode":"([^"]*)"/g) {
            push @iflist, $1;
        }
    }
    return ($status, \@iflist);
}

# Method ===============================================================
#
=item B<get_interfaces_cfg>

  # Returns   : BSC::Status
  #           : VR5600 network interface configuration as JSON string

=cut ===================================================================
sub get_interfaces_cfg {
    my $self = shift;
    my $status = new Brocade::BSC::Status;
    my $config = undef;

    my $urlpath = $self->{ctrl}->get_ext_mount_config_urlpath($self->{name});
    $urlpath .= "vyatta-interfaces:interfaces";

    my $resp = $self->ctrl_req('GET', $urlpath);
    if ($resp->code == HTTP_OK) {
        $config = $resp->content;
        $status->code($BSC_OK);
    }
    else {
        $status->http_err($resp);
    }
    return ($status, $config);
}

# Method ===============================================================
#
=item B<get_dataplane_interfaces_list>

  # Returns   : BSC::Status
  #           : array - dataplane interface names

=cut ===================================================================
sub get_dataplane_interfaces_list {
    my $self = shift;
    my $status = new Brocade::BSC::Status;
    my $dpifcfg = undef;
    my $iflist = undef;
    my @dpiflist;

    ($status, $dpifcfg) = $self->get_interfaces_cfg();
    if (! $dpifcfg) {
        $status->code($BSC_DATA_NOT_FOUND);
    }
    else {
        $iflist = decode_json($dpifcfg)->{interfaces}->{'vyatta-interfaces-dataplane:dataplane'};
        foreach my $interface (@$iflist) {
            push @dpiflist, $interface->{tagnode};
        }
        $status->code($BSC_OK);
    }
    return ($status, @dpiflist);
}

# Method ===============================================================
#
=item B<get_dataplane_interfaces_cfg>

  # Returns   : BSC::Status
  #           : array ref - configuration of all dataplane interfaces

=cut ===================================================================
sub get_dataplane_interfaces_cfg {
    my $self = shift;
    my $dpifcfg = undef;

    my ($status, $config) = $self->get_interfaces_cfg();
    if ($status->ok) {
        my $str1 = 'interfaces';
        my $str2 = 'vyatta-interfaces-dataplane:dataplane';
        if ($config =~ /$str2/) {
            $dpifcfg = decode_json($config)->{$str1}->{$str2};
        }
    }
    return ($status, $dpifcfg);
}

# Method ===============================================================
#
=item B<get_dataplane_interface_cfg>

  # Parameters: name of interface
  # Returns   : BSC::Status
  #           : configuration of specified interface as JSON string

=cut ===================================================================
sub get_dataplane_interface_cfg {
    my $self = shift;
    my $ifname = shift;
    my $status = new Brocade::BSC::Status;
    my $cfg = undef;

    my $urlpath = $self->{ctrl}->get_ext_mount_config_urlpath($self->{name})
        . "vyatta-interfaces:interfaces/vyatta-interfaces-dataplane:dataplane/"
        . $ifname;
    my $resp = $self->ctrl_req('GET', $urlpath);
    if ($resp->code == HTTP_OK) {
        $cfg = $resp->content;
        $status->code($BSC_OK);
    }
    else {
        $status->http_err($resp);
    }
    return ($status, $cfg);
}

# Method ===============================================================
#
=item B<get_loopback_interfaces_list>

  # Returns   : BSC::Status
  #           : array ref - loopback interface names

=cut ===================================================================
sub get_loopback_interfaces_list {
    my $self = shift;
    my @lbiflist = ();

    my ($status, $lbifcfg) = $self->get_loopback_interfaces_cfg();
    if (! $lbifcfg) {
        $status->code($BSC_DATA_NOT_FOUND);
    }
    else {
        foreach (@$lbifcfg) {
            push @lbiflist, $_->{tagnode};
        }
        $status->code($BSC_OK);
    }
    return ($status, \@lbiflist);
}

# Method ===============================================================
#
=item B<get_loopback_interfaces_cfg>

  # Returns   : BSC::Status
  #           : array ref - configuration of loopback interfaces

=cut ===================================================================
sub get_loopback_interfaces_cfg {
    my $self = shift;
    my $lbifcfg = undef;

    my ($status, $config) = $self->get_interfaces_cfg();
    if ($status->ok) {
        my $str1 = 'interfaces';
        my $str2 = 'vyatta-interfaces-loopback:loopback';
        if (($config =~ /$str1/) && ($config =~ /$str2/)) {
            $lbifcfg = decode_json($config)->{$str1}->{$str2};
        }
    }
    return ($status, $lbifcfg);
}

# Method ===============================================================
#
=item B<get_loopback_interface_cfg>

  # Parameters: name of interface
  # Returns   : BSC::Status
  #           : requested loopback configuration as JSON string

=cut ===================================================================
sub get_loopback_interface_cfg {
    my $self = shift;
    my $ifName = shift;
    my $status = new Brocade::BSC::Status($BSC_OK);
    my $config = undef;

    my $urlpath = $self->{ctrl}->get_ext_mount_config_urlpath($self->{name})
        . "vyatta-interfaces:interfaces/vyatta-interfaces-loopback:loopback/"
        . $ifName;
    my $resp = $self->ctrl_req('GET', $urlpath);
    if ($resp->code == HTTP_OK) {
        $config = $resp->content;
        $status->code($BSC_OK);
    }
    else {
        $status->http_err($resp);
    }

    return ($status, $config);
}


# Method ===============================================================
#
=item B<set_vpn_cfg>

  # Parameters: BSC::Node::NC::Vrouter::VPN
  # Returns   : BSC::Status

=cut ===================================================================
sub set_vpn_cfg {
    my ($self, $vpn) = @_;
    my $status = new Brocade::BSC::Status($BSC_OK);

    my $urlpath = $self->{ctrl}->get_ext_mount_config_urlpath($self->{name});
    my %headers = ('content-type' => 'application/yang.data+json');

    my $resp = $self->ctrl_req('POST', $urlpath, $vpn->get_payload(), \%headers);
    $resp->is_success or $status->http_err($resp);
    return $status;
}


# Method ===============================================================
#
=item B<get_vpn_cfg>

  # Returns   : BSC::Status
  #           : VPN configuration as JSON string

=cut ===================================================================
sub get_vpn_cfg {
    my $self = shift;
    my $status = new Brocade::BSC::Status;
    my $config = undef;
    
    my $urlpath = $self->{ctrl}->get_ext_mount_config_urlpath($self->{name})
	. "vyatta-security:security/vyatta-security-vpn-ipsec:vpn";
    my $resp = $self->ctrl_req('GET', $urlpath);
    if ($resp->code == HTTP_OK) {
	$config = $resp->content;
	$status->code($BSC_OK);
    }
    elsif ($resp->code == HTTP_NOT_FOUND) {
	$status->code($BSC_DATA_NOT_FOUND);
    }
    else {
	$status->http_err($resp);
    }

    return ($status, $config);
}


# Method ===============================================================
#
=item B<delete_vpn_cfg>

  # Parameters: none - deletes all vpn configuration
  # Returns   : BSC::Status

=cut ===================================================================
sub delete_vpn_cfg {
    my $self = shift;
    my $status = new Brocade::BSC::Status($BSC_OK);

    my $urlpath = $self->{ctrl}->get_ext_mount_config_urlpath($self->{name})
	. "vyatta-security:security/vyatta-security-vpn-ipsec:vpn";
    my $resp = $self->ctrl_req('DELETE', $urlpath);
    $resp->is_success() or $status->http_err($resp);
    return $status;
}


# Module ===============================================================
1;

=back

=head1 LICENCE AND COPYRIGHT

Copyright (c) 2015,  BROCADE COMMUNICATIONS SYSTEMS, INC

All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:

1. Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.

2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.

3. Neither the name of the copyright holder nor the names of its
contributors may be used to endorse or promote products derived from this
software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
THE POSSIBILITY OF SUCH DAMAGE.
