Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP

Comparing changes

Choose two branches to see what's changed or to start a new pull request. If you need to, you can also compare across forks.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also compare across forks.
...
Checking mergeability… Don't worry, you can still create the pull request.
  • 17 commits
  • 9 files changed
  • 2 commit comments
  • 2 contributors
Commits on May 10, 2012
@obilodeau obilodeau View::DATA is now View::JSON e829db5
@cgx cgx Added Interface Model/Controller.
Cherry-pick from d649883 and reworked a bit to put under the Config namespace
6c622a7
Commits on May 11, 2012
@obilodeau obilodeau basic actions work now bc17b92
@obilodeau obilodeau reindent + exception handling in chained dispatch 9c71a8b
@obilodeau obilodeau rework of the Config::Interface system
- only one Model: Config::Pf
- indentation
- JSON view filter in configuration
- logged warnings
- Controller error handling
31cdc84
@obilodeau obilodeau refactoring
- mass-rename section to interface
- got rid of sectionExists requirement (chain only assigns to stash)
35d83bc
@obilodeau obilodeau POD metadata changes ef55a86
@obilodeau obilodeau converted HTTP status into constants 5f85c81
@obilodeau obilodeau from AGED to CRUD 4c40913
@obilodeau obilodeau no longer dying on 'trappable' errors 1b17752
Commits on May 16, 2012
@obilodeau obilodeau introduced new error handling strategy for Controler / Model f67452e
Commits on May 17, 2012
@obilodeau obilodeau $result to $status d37886a
@obilodeau obilodeau refactoring (exception, practices) + create works
* no JSON in entry of data, only POST key=value
* added _ref to references
* newer shinier try {} catch {}; syntax
32ebb43
@obilodeau obilodeau using columns orders provided by ui.conf -- preview 868f149
Commits on May 18, 2012
@obilodeau obilodeau pf::errors is now pf::error 25fcfc3
@obilodeau obilodeau Merge branch 'feature/catalyst-configurator-config-services' into fea…
…ture/catalyst-configurator
3938265
@obilodeau obilodeau POD fix in pf::error 0733ee6
View
16 html/configurator/lib/configurator.pm
@@ -3,6 +3,7 @@ use Moose;
use namespace::autoclean;
use Catalyst::Runtime 5.80;
+use Log::Log4perl::Catalyst;
# Set flags and add plugins for the application
#
@@ -20,6 +21,7 @@ use Catalyst qw/
Session
Session::Store::File
Session::State::Cookie
+ StackTrace
StatusMessage
/;
@@ -52,8 +54,22 @@ __PACKAGE__->config(
'Plugin::Session' => {
storage => '/usr/local/pf/var/session'
},
+
+ 'View::JSON' => {
+ allow_callback => 1, # defaults to 0
+ callback_param => 'cb', # defaults to 'callback'
+ expose_stash => [ qw(status_msg error interfaces switches) ], # defaults to everything
+ },
);
+# Logging
+# TODO define a logging strategy that would fit both catalyst and our core
+# application. For now, it's all basic
+__PACKAGE__->log(Log::Log4perl::Catalyst->new());
+#__PACKAGE__->log(Log::Log4perl::Catalyst->new(__PACKAGE__->path_to('Log4perl.conf')->stringify ) );
+# Handle warnings from Perl as fatal log messages
+$SIG{__WARN__} = sub { __PACKAGE__->log->error(@_); };
+
# Start the application
__PACKAGE__->setup();
View
185 html/configurator/lib/configurator/Controller/Config/Interface.pm
@@ -0,0 +1,185 @@
+package configurator::Controller::Config::Interface;
+use HTTP::Status qw(:constants is_error);
+use JSON;
+use Moose;
+use namespace::autoclean;
+use Try::Tiny;
+
+BEGIN { extends 'Catalyst::Controller'; }
+
+=head1 NAME
+
+pfws::Controller::Config::Interface - Catalyst Controller
+
+=head1 DESCRIPTION
+
+Catalyst Controller.
+
+=head1 METHODS
+
+=head2 index
+
+=cut
+sub index :Path :Args(0) {
+ my ( $self, $c ) = @_;
+
+ $c->visit('read', ['all'], ['read']);
+}
+
+=head2 object
+
+Chained dispatch for an interface.
+
+=cut
+sub object :Chained('/') :PathPart('config/interface') :CaptureArgs(1) {
+ my ($self, $c, $interface) = @_;
+ $c->stash->{interface} = $interface;
+}
+
+=head2 read
+
+/config/interface/<interface>/read
+
+=cut
+sub read :Chained('object') :PathPart('read') :Args(0) {
+ my ($self, $c) = @_;
+ my $interface = $c->stash->{interface};
+
+ my ($status, $message) = $c->model('Config::Pf')->read_interface($interface);
+ if (is_error($status)) {
+ $c->res->status($status);
+ $c->error($message);
+ }
+ else {
+ $c->stash->{interfaces} = $message;
+ }
+}
+
+=head2 delete
+
+/config/interface/<interface>/delete
+
+=cut
+sub delete :Chained('object') :PathPart('delete') :Args(0) {
+ my ($self, $c) = @_;
+ my $interface = $c->stash->{interface};
+
+ my ($status, $message) = $c->model('Config::Pf')->delete_interface($interface);
+ if (is_error($status)) {
+ $c->res->status($status);
+ $c->error($message);
+ }
+ else {
+ $c->stash->{status_msg} = $message;
+ }
+}
+
+=head2 update
+
+/config/interface/<interface>/update
+
+=cut
+sub update :Chained('object') :PathPart('update') :Args(0) {
+ my ($self, $c) = @_;
+ my $interface = $c->stash->{interface};
+
+ my $assignments_ref = $c->request->body_params->{assignments};
+
+ if ($assignments_ref) {
+ my $decoded_assignments_ref = try { return decode_json($assignments_ref); }
+ catch {
+ # Malformed JSON
+ chomp $_;
+ $c->res->status(HTTP_BAD_REQUEST);
+ $c->stash->{status_msg} = $_;
+ return;
+ };
+ if (defined($decoded_assignments_ref)) {
+ my ($status, $message) = $c->model('Config::Pf')->update_interface($interface, $assignments_ref);
+ if (is_error($status)) {
+ $c->res->status($status);
+ $c->error($message);
+ }
+ else {
+ $c->res->status(HTTP_CREATED);
+ $c->stash->{status_msg} = $message;
+ }
+ }
+ }
+ else {
+ $c->res->status(HTTP_BAD_REQUEST);
+ $c->stash->{status_msg} = 'Missing parameters';
+ }
+}
+
+=head2 create
+
+/config/interface/create/<interface>
+
+=cut
+
+sub create :Chained('object') :PathPart('create') :Args(0) {
+ my ($self, $c) = @_;
+ my $interface = $c->stash->{interface};
+
+ my $assignments_ref = $c->request->body_params;
+ if (defined($assignments_ref)) {
+ my ($status, $message) = $c->model('Config::Pf')->create_interface($interface, $assignments_ref);
+ if (is_error($status)) {
+ $c->res->status($status);
+ $c->error($message);
+ }
+ else {
+ $c->res->status(HTTP_CREATED);
+ $c->stash->{status_msg} = $message;
+ }
+ }
+ else {
+ $c->res->status(HTTP_BAD_REQUEST);
+ $c->stash->{status_msg} = 'Missing parameters';
+ }
+}
+
+sub end : ActionClass('RenderView') {
+ my ( $self, $c ) = @_;
+ # TODO In DEVEL that's cool, but in production we want only a 500 generic message and logging on 'unhandled' errors
+ if ( scalar @{ $c->error } ) {
+ $c->stash->{status_msg} = $c->error;
+ $c->forward('View::JSON');
+ $c->error(0);
+ }
+ $c->forward('View::JSON');
+}
+
+=head1 AUTHOR
+
+Francis Lachapelle <flachapelle@inverse.ca>
+
+Olivier Bilodeau <obilodeau@inverse.ca>
+
+=head1 COPYRIGHT
+
+Copyright 2012 Inverse inc.
+
+=head1 LICENSE
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
+USA.
+
+=cut
+
+__PACKAGE__->meta->make_immutable;
+
+1;
View
44 html/configurator/lib/configurator/Controller/Config/Pf.pm
@@ -0,0 +1,44 @@
+package configurator::Controller::Config::Pf;
+use Moose;
+use namespace::autoclean;
+
+BEGIN {extends 'Catalyst::Controller'; }
+
+=head1 NAME
+
+configurator::Controller::Config::Pf - Catalyst Controller
+
+=head1 DESCRIPTION
+
+Catalyst Controller.
+
+=head1 METHODS
+
+=cut
+
+
+=head2 index
+
+=cut
+
+sub index :Path :Args(0) {
+ my ( $self, $c ) = @_;
+
+ $c->response->body('Matched configurator::Controller::Config::Pf in Config::Pf.');
+}
+
+
+=head1 AUTHOR
+
+root
+
+=head1 LICENSE
+
+This library is free software. You can redistribute it and/or modify
+it under the same terms as Perl itself.
+
+=cut
+
+__PACKAGE__->meta->make_immutable;
+
+1;
View
205 html/configurator/lib/configurator/Model/Config/Pf.pm
@@ -0,0 +1,205 @@
+package configurator::Model::Config::Pf;
+use Moose;
+use namespace::autoclean;
+
+use Config::IniFiles;
+
+use pf::config;
+use pf::config::ui;
+use pf::error;
+
+extends 'Catalyst::Model';
+
+=head1 NAME
+
+pfws::Model::Config::Pf - Catalyst Model
+
+=head1 DESCRIPTION
+
+Catalyst Model.
+
+=head1 METHODS
+
+=cut
+
+my $_pf_conf = undef;
+
+=item _pf_conf
+
+Load pf.conf into a Config::IniFiles tied hashref
+
+=cut
+sub _pf_conf {
+ my ($self) = @_;
+ my $logger = Log::Log4perl::get_logger(__PACKAGE__);
+
+ unless (defined $_pf_conf) {
+ my %conf;
+ tie %conf, 'Config::IniFiles', ( -file => "$conf_dir/pf.conf" );
+ my @errors = @Config::IniFiles::errors;
+ if ( scalar(@errors) || !%conf ) {
+ $logger->logdie("Error reading pf.conf: " . join( "\n", @errors ) . "\n" );
+ }
+
+ foreach my $section ( tied(%conf)->Sections ) {
+ foreach my $key ( keys %{ $conf{$section} } ) {
+ $conf{$section}{$key} =~ s/\s+$//;
+ }
+ }
+ $_pf_conf = \%conf;
+ }
+
+ return $_pf_conf;
+}
+
+sub read_interface {
+ my ($self, $interface) = @_;
+ my $logger = Log::Log4perl::get_logger(__PACKAGE__);
+
+ $logger->debug("interface $interface requested");
+
+ my $pf_conf = $self->_pf_conf();
+ my @columns = pf::config::ui->instance->field_order('interfaceconfig get');
+ my @resultset = @columns;
+ foreach my $s ( keys %$pf_conf ) {
+ if ( $s =~ /^interface (.+)$/ ) {
+ my $interface_name = $1;
+ if ( ( $interface eq 'all' ) || ( $interface eq $interface_name ) ) {
+ my @values;
+ foreach my $column (@columns) {
+ push @values, ( $pf_conf->{$s}->{$column} || '' );
+ }
+ push @resultset, [$interface_name, @values];
+ }
+ }
+ }
+
+ if ($#resultset > 0) {
+ return ($STATUS::OK, \@resultset);
+ }
+ else {
+ return ($STATUS::NOT_FOUND, "Unknown interface $interface");
+ }
+}
+
+sub delete_interface {
+ my ($self, $interface) = @_;
+ my $logger = Log::Log4perl::get_logger(__PACKAGE__);
+
+ return ($STATUS::FORBIDDEN, "This interface can't be deleted") if ( $interface eq 'all' );
+
+ my $interface_name = "interface $interface";
+ my $pf_conf = $self->_pf_conf();
+ my $tied_conf = tied(%$pf_conf);
+ if ( $tied_conf->SectionExists($interface_name) ) {
+ $tied_conf->DeleteSection($interface_name);
+ $tied_conf->WriteConfig($conf_dir . "/pf.conf")
+ or $logger->logdie(
+ "Unable to write config to $conf_dir/pf.conf. "
+ ."You might want to check the file's permissions."
+ );
+ # The following snippet updates the database
+ require pf::configfile;
+ import pf::configfile;
+ configfile_import( $conf_dir . "/pf.conf" );
+ } else {
+ return ($STATUS::NOT_FOUND, "Interface not found");
+ }
+
+ return ($STATUS::OK, "Successfully deleted $interface");
+}
+
+sub update_interface {
+ my ($self, $interface, $assignments) = @_;
+ my $logger = Log::Log4perl::get_logger(__PACKAGE__);
+
+ return ($STATUS::FORBIDDEN, "This interface can't be updated") if ( $interface eq 'all' );
+
+ my $interface_name = "interface $interface";
+ my $pf_conf = $self->_pf_conf();
+ my $tied_conf = tied(%$pf_conf);
+ if ( $tied_conf->SectionExists($interface_name) ) {
+ while (my ($param, $value) = each %$assignments) {
+ if ( defined( $pf_conf->{$interface_name}{$param} ) ) {
+ $tied_conf->setval( $interface_name, $param, $value );
+ } else {
+ $tied_conf->newval( $interface_name, $param, $value );
+ }
+ }
+ $tied_conf->WriteConfig($conf_dir . "/pf.conf")
+ or $logger->logdie(
+ "Unable to write config to $conf_dir/pf.conf. "
+ ."You might want to check the file's permissions."
+ );
+ # The following snippet updates the database
+ require pf::configfile;
+ import pf::configfile;
+ configfile_import( $conf_dir . "/pf.conf" );
+ } else {
+ return ($STATUS::NOT_FOUND, "Interface not found");
+ }
+
+ return ($STATUS::OK, "Successfully modified $interface");
+}
+
+sub create_interface {
+ my ($self, $interface, $assignments) = @_;
+ my $logger = Log::Log4perl::get_logger(__PACKAGE__);
+
+ return ($STATUS::FORBIDDEN, "This is a reserved interface name") if ( $interface eq 'all' );
+
+ my $interface_name = "interface $interface";
+ my $pf_conf = $self->_pf_conf();
+ my $tied_conf = tied(%$pf_conf);
+ if ( !($tied_conf->SectionExists($interface_name)) ) {
+ while (my ($param, $value) = each %$assignments) {
+ $tied_conf->AddSection($interface_name);
+ $tied_conf->newval( $interface_name, $param, $value );
+ }
+ $tied_conf->WriteConfig($conf_dir . "/pf.conf")
+ or $logger->logdie(
+ "Unable to write config to $conf_dir/pf.conf. "
+ ."You might want to check the file's permissions."
+ );
+ require pf::configfile;
+ import pf::configfile;
+ configfile_import( $conf_dir . "/pf.conf" );
+ } else {
+ return ($STATUS::PRECONDITION_FAILED, "Interface $interface already exists");
+ }
+
+ return ($STATUS::OK, "Successfully created $interface");
+}
+
+=head1 AUTHOR
+
+Francis Lachapelle <flachapelle@inverse.ca>
+
+Olivier Bilodeau <obilodeau@inverse.ca>
+
+=head1 COPYRIGHT
+
+Copyright 2012 Inverse inc.
+
+=head1 LICENSE
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
+USA.
+
+=cut
+
+__PACKAGE__->meta->make_immutable;
+
+1;
View
10 html/configurator/t/controller_Config-Interface.t
@@ -0,0 +1,10 @@
+use strict;
+use warnings;
+use Test::More;
+
+
+use Catalyst::Test 'pfws';
+use configurator::Controller::Config::Interface;
+
+ok( request('/interface')->is_success, 'Request should succeed' );
+done_testing();
View
14 html/configurator/t/controller_Config-Pf.t
@@ -0,0 +1,14 @@
+use strict;
+use warnings;
+use Test::More;
+
+
+unless (eval q{use Test::WWW::Mechanize::Catalyst 'Config::Pf'; 1}) {
+ plan skip_all => 'Test::WWW::Mechanize::Catalyst required';
+ exit 0;
+}
+
+ok( my $mech = Test::WWW::Mechanize::Catalyst->new, 'Created mech object' );
+
+$mech->get_ok( 'http://localhost/config/pf' );
+done_testing();
View
8 html/configurator/t/model_Config-Pf.t
@@ -0,0 +1,8 @@
+use strict;
+use warnings;
+use Test::More;
+use Test::More;
+
+BEGIN { use_ok 'configurator::Model::Config::Pf' }
+
+done_testing();
View
164 lib/pf/config/ui.pm
@@ -0,0 +1,164 @@
+package pf::config::ui;
+
+=head1 NAME
+
+pf::config::ui - OO module that holds the ui.conf configuration
+
+=head1 SYNOPSIS
+
+The pf::config::ui OO module holds the content of conf/ui.conf hot in memory.
+
+=head1 DEVELOPER NOTES
+
+Singleton patterns means you should not keep state within this module.
+
+=cut
+
+=head1 FILES
+
+conf/ui.conf
+
+=cut
+use strict;
+use warnings;
+
+use Log::Log4perl;
+
+our $VERSION = 1.00;
+
+use pf::config;
+
+my $singleton;
+
+=head1 SUBROUTINES
+
+=over
+
+=item instance
+
+Get the singleton instance of pf::config::ui. Create it if it doesn't exist.
+
+=cut
+sub instance {
+ my ( $class, %args ) = @_;
+
+ if (!defined($singleton)) {
+ $singleton = $class->new(%args);
+ }
+
+ return $singleton;
+}
+
+=item new
+
+Constructor. Usually you don't want to call this constructor but use the
+pf::config::ui::custom subclass instead.
+
+=cut
+
+sub new {
+ my ( $class, %argv ) = @_;
+ my $logger = Log::Log4perl::get_logger(__PACKAGE__);
+ $logger->debug("instantiating new " . __PACKAGE__ . " object");
+ my $self = bless {}, $class;
+ return $self;
+}
+
+=back
+
+=head1 METHODS
+
+=over
+
+=cut
+my $_ui_conf_tie = undef;
+
+=item _ui_conf
+
+Load ui.conf into a Config::IniFiles tied hashref
+
+=cut
+sub _ui_conf {
+ my ($self) = @_;
+ my $logger = Log::Log4perl::get_logger(__PACKAGE__);
+
+ unless (defined $_ui_conf_tie) {
+ my %conf;
+ tie %conf, 'Config::IniFiles', ( -file => "$conf_dir/ui.conf" );
+ my @errors = @Config::IniFiles::errors;
+ if ( scalar(@errors) || !%conf ) {
+ $logger->logdie("Error reading ui.conf: " . join( "\n", @errors ) . "\n" );
+ }
+
+ $_ui_conf_tie = \%conf;
+ }
+
+ return $_ui_conf_tie;
+}
+
+=item field_order
+
+Return the correct field order listed in ui.conf for a given resource.
+
+Ex resources:
+
+ interfaceconfig get
+
+=cut
+# TODO there is caching opportunity here
+# TODO once bin/pfcmd is only a web services client we can get rid of
+# $resource and do auto-lookup based on caller and get rid of all
+# hard-coded names
+sub field_order {
+ my ($self, $resource) = @_;
+ my $logger = Log::Log4perl::get_logger(__PACKAGE__);
+
+ my $uiconfig = $self->_ui_conf();
+ my @fields;
+ foreach my $section ( sort tied(%$uiconfig)->Sections ) {
+
+ # skipping sections without command
+ next if (!defined($uiconfig->{$section}->{command}));
+
+ if ($resource =~ /^$uiconfig->{$section}->{command}/) {
+
+ foreach my $val ( split( /\s*,\s*/, $uiconfig->{$section}->{'display'} ) ) {
+ $val =~ s/-//;
+ push @fields, $val;
+ }
+ last;
+ }
+ }
+ return (@fields);
+}
+
+=back
+
+=head1 AUTHOR
+
+Olivier Bilodeau <obilodeau@inverse.ca>
+
+=head1 COPYRIGHT
+
+Copyright (C) 2012 Inverse inc.
+
+=head1 LICENSE
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
+USA.
+
+=cut
+
+1;
View
116 lib/pf/error.pm
@@ -0,0 +1,116 @@
+package pf::error;
+
+=head1 NAME
+
+pf::error - Error codes and related functions
+
+=head1 DESCRIPTION
+
+Error codes constants and related error-handling and reporting utilities.
+
+=cut
+use strict;
+use warnings;
+
+use Readonly;
+
+package STATUS;
+=head1 Status codes
+
+We rely on HTTP status codes for our Web Services and decided to use them
+in our Model without the mention of HTTP in front.
+
+Taken from HTTP::Status and stripped. Subject to change.
+
+=cut
+Readonly::Scalar our $OK => 200;
+Readonly::Scalar our $CREATED => 201;
+#Readonly::Scalar our $ACCEPTED => 202;
+#Readonly::Scalar our $NON_AUTHORITATIVE_INFORMATION => 203;
+#Readonly::Scalar our $NO_CONTENT => 204;
+#Readonly::Scalar our $RESET_CONTENT => 205;
+#Readonly::Scalar our $PARTIAL_CONTENT => 206;
+#Readonly::Scalar our $MULTI_STATUS => 207;
+#Readonly::Scalar our $ALREADY_REPORTED => 208;
+
+#Readonly::Scalar our $MULTIPLE_CHOICES => 300;
+#Readonly::Scalar our $MOVED_PERMANENTLY => 301;
+#Readonly::Scalar our $FOUND => 302;
+#Readonly::Scalar our $SEE_OTHER => 303;
+#Readonly::Scalar our $NOT_MODIFIED => 304;
+#Readonly::Scalar our $USE_PROXY => 305;
+#Readonly::Scalar our $TEMPORARY_REDIRECT => 307;
+
+#Readonly::Scalar our $BAD_REQUEST => 400;
+#Readonly::Scalar our $UNAUTHORIZED => 401;
+#Readonly::Scalar our $PAYMENT_REQUIRED => 402;
+Readonly::Scalar our $FORBIDDEN => 403;
+Readonly::Scalar our $NOT_FOUND => 404;
+#Readonly::Scalar our $METHOD_NOT_ALLOWED => 405;
+#Readonly::Scalar our $NOT_ACCEPTABLE => 406;
+#Readonly::Scalar our $PROXY_AUTHENTICATION_REQUIRED => 407;
+#Readonly::Scalar our $REQUEST_TIMEOUT => 408;
+#Readonly::Scalar our $CONFLICT => 409;
+#Readonly::Scalar our $GONE => 410;
+#Readonly::Scalar our $LENGTH_REQUIRED => 411;
+Readonly::Scalar our $PRECONDITION_FAILED => 412;
+#Readonly::Scalar our $REQUEST_ENTITY_TOO_LARGE => 413;
+#Readonly::Scalar our $REQUEST_URI_TOO_LARGE => 414;
+#Readonly::Scalar our $UNSUPPORTED_MEDIA_TYPE => 415;
+#Readonly::Scalar our $REQUEST_RANGE_NOT_SATISFIABLE => 416;
+#Readonly::Scalar our $EXPECTATION_FAILED => 417;
+#Readonly::Scalar our $I_AM_A_TEAPOT => 418;
+#Readonly::Scalar our $UNPROCESSABLE_ENTITY => 422;
+#Readonly::Scalar our $LOCKED => 423;
+#Readonly::Scalar our $FAILED_DEPENDENCY => 424;
+#Readonly::Scalar our $NO_CODE => 425;
+#Readonly::Scalar our $UPGRADE_REQUIRED => 426;
+#Readonly::Scalar our $PRECONDITION_REQUIRED => 428;
+#Readonly::Scalar our $TOO_MANY_REQUESTS => 429;
+#Readonly::Scalar our $REQUEST_HEADER_FIELDS_TOO_LARGE => 431;
+#Readonly::Scalar our $RETRY_WITH => 449;
+
+#Readonly::Scalar our $INTERNAL_SERVER_ERROR => 500;
+#Readonly::Scalar our $NOT_IMPLEMENTED => 501;
+#Readonly::Scalar our $BAD_GATEWAY => 502;
+#Readonly::Scalar our $SERVICE_UNAVAILABLE => 503;
+#Readonly::Scalar our $GATEWAY_TIMEOUT => 504;
+#Readonly::Scalar our $HTTP_VERSION_NOT_SUPPORTED => 505;
+#Readonly::Scalar our $VARIANT_ALSO_NEGOTIATES => 506;
+#Readonly::Scalar our $INSUFFICIENT_STORAGE => 507;
+#Readonly::Scalar our $BANDWIDTH_LIMIT_EXCEEDED => 509;
+#Readonly::Scalar our $NOT_EXTENDED => 510;
+#Readonly::Scalar our $NETWORK_AUTHENTICATION_REQUIRED => 511;
+
+=head1 AUTHOR
+
+Olivier Bilodeau <obilodeau@inverse.ca>
+
+=head1 COPYRIGHT
+
+Copyright (C) 2012 Inverse inc.
+
+=head1 LICENSE
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
+USA.
+
+=cut
+
+1;
+
+# vim: set shiftwidth=4:
+# vim: set expandtab:
+# vim: set backspace=indent,eol,start:

Showing you all comments on commits in this comparison.

@dwlfrth
Owner

Should be pf::error rather than pf::errors !!

@obilodeau

done

Something went wrong with that request. Please try again.