File Coverage

blib/lib/Firewall/PaloAlto/Panorama.pm
Criterion Covered Total %
statement 1 3 33.3
branch n/a
condition n/a
subroutine 1 1 100.0
pod n/a
total 2 4 50.0


line stmt bran cond sub pod time code
1             package Firewall::PaloAlto::Panorama;
2 2     2   34575 use Moose;
  0            
  0            
3              
4             use 5.006;
5             use strict;
6             use warnings;
7              
8             use XML::Simple;
9             use LWP::UserAgent;
10             use Carp;
11             use Data::Dumper qw(Dumper);
12             use Modern::Perl;
13             use Params::Validate qw(:all);
14              
15             use List::Flatten;
16              
17             with 'Firewall::PaloAlto::Common';
18              
19             our $VERSION = '0.02';
20              
21              
22             =head1 NAME
23              
24             Firewall::PaloAlto::Panorama - Interact with a Palo Alto Panorama controller's API through Perl.
25              
26             =cut
27              
28             =head1 SYNOPSIS
29              
30             The Firewall::PaloAlto::Panorama module provides interfaces into the XML API of a Palo Alto Panorama contoller.
31              
32             use Firewall::PaloAlto::Panorama;
33              
34             my $fw = Firewall::PaloAlto::Panorama->new(host => 'panorama.local', username => 'admin', password => 'admin');
35             $fw->connect();
36             =cut
37              
38             =head1 CLASS METHODS
39              
40             =head2 new(%parameters)
41              
42             The constructor generates a new object to interact with a specific firewall.
43              
44             The host, username and password parameters are mandatory.
45             If not specified, SSL is used, but it can be disabled using the argument ssl => 0
46              
47             Detailed debugging can be turned on using the debu => 1 argument. It is off by default.
48              
49             my $palo = Firewall::PaloAlto::Panorama->new(host => 'panorama.local', username => 'admin', password => 'admin', ssl => 0, debug => 1);
50              
51             B
52              
53             =over
54              
55             =item *
56             host - the hostname or IP of the Panorama to connect to.
57              
58             =item *
59             username - a username to connect to the Panorama.
60              
61             =item *
62             password - a password to connect to the Panorama.
63              
64             =item *
65             ssl (optional, default: 1) - use SSL to connect to the firewall.
66              
67             =item *
68             debug (optional, default: 0) - print debugging messages.
69              
70             =back
71              
72             =cut
73              
74              
75             =head1 OBJECT METHODS
76              
77             =head2 connect()
78              
79             The connect functions connects to the Palo Alto, validates and saves the API key.
80             It has no parameters
81              
82             $pa->connect();
83              
84             =cut
85              
86             sub connect {
87             my $self = shift;
88              
89             my $user_agent = LWP::UserAgent->new(ssl_opts => { verify_hostname => 0 });
90             my $key_request = HTTP::Request->new(GET => $self->base_uri.'type=keygen&user='.$self->username.'&password='.$self->password);
91              
92             $self->_debug_print("Key Request", "");
93              
94             my $key_response = $user_agent->request($key_request);
95              
96             my $pa_response = $self->_check_response_and_parse($key_response);
97              
98             if ($pa_response->{status} eq 'success') {
99             $self->_api_key($pa_response->{result}->{key});
100             return 1;
101             }
102             else {
103             return 0;
104             }
105             }
106              
107             =head2 managed_device($action, serial => 'device_serial')
108              
109             Adds or removes a devices from Panorama.
110              
111             $pano->managed_device('set', serial => '001801003449');
112              
113             =cut
114              
115             sub managed_device {
116             my $self = shift;
117             my $action = shift;
118             my (%args) = @_;
119              
120             if ($action eq 'set' || $action eq 'get' || $action eq 'delete') {
121             validate(@_, { serial => 1 });
122             }
123             else { croak "Unknown action: $action"; }
124              
125             my $requester = $self->_create_requester(type => 'config', action => $action);
126              
127             $requester->(xpath => "/config/mgt-config/devices", element => "");
128             }
129            
130              
131             =head2 template($action, %parameters)
132              
133             Creates or retreives a new template
134              
135             $pano->template('set', name => "New_Template");
136              
137             B
138              
139             =over
140              
141             =item * $action - perform an action: ['get' | 'set' | 'delete']
142              
143             =item * name - the name of the template
144              
145             =item * description - a description of the template
146              
147             =back
148              
149             =cut
150              
151             sub template {
152             my $self = shift;
153             my $action = shift;
154             my (%args) = @_;
155              
156             if ($action eq 'set') {
157             validate(@_, { name => 1, description => 0 });
158             #If no description supplied, the description is set to nothing.
159             $args{description} //= $args{name};
160             }
161             elsif ($action eq 'get' or $action eq 'delete') {
162             validate(@_, { name => 1 })
163             }
164             else { croak "Unknown action: $action"; }
165              
166              
167             my $template_name = delete $args{name};
168             my $requester = $self->_create_requester(type => 'config', action => $action);
169              
170             my $elements = $self->_generate_elements(%args);
171              
172             return $requester->(xpath => "/config/devices/entry[\@name='localhost.localdomain']/template/entry[\@name='$template_name']", element => $elements);
173             }
174              
175              
176             =head2 template_stack($action, %parameters)
177              
178             Creates or retreives a new template stack.
179              
180             $pano->template_stack('set', name => "New_Template", templates => [ Template1, Template2 ], description => "A Template Stack");
181              
182             B
183              
184             =over
185              
186             =item * $action - perform an action: ['get' | 'set' | 'delete']
187              
188             =item * name - the name of the template stack
189              
190             =item * templates - an anonymous array of templates that make up the stack
191              
192             =item * description - a description of the template
193              
194             =back
195              
196             =cut
197              
198             sub template_stack {
199             my $self = shift;
200             my $action = shift;
201             my (%args) = @_;
202              
203             if ($action eq 'set') {
204             validate(@_, { name => 1, templates => { type => ARRAYREF }, description => 0 });
205             }
206             elsif ($action eq 'get' or $action eq 'delete') {
207             validate(@_, { name => 1 })
208             }
209             else { croak "Unknown action: $action"; }
210              
211             my $stack_name = delete $args{name};
212             my $requester = $self->_create_requester(type => 'config', action => $action);
213              
214             my $elements = $self->_generate_elements(%args);
215              
216             return $requester->(xpath => "/config/devices/entry[\@name='localhost.localdomain']/template-stack/entry[\@name='$stack_name']", element => $elements);
217             }
218              
219             =head2 devicegroup($action, %parameters)
220              
221             Creates or retreives a new device group.
222              
223             $pano->devicegroup('set', name => "DG_Name", description => "A Device Group");
224              
225             B
226              
227             =over
228              
229             =item * $action - perform an action: ['get' | 'set' | 'delete']
230              
231             =item * name - the name of the device group
232              
233             =item * description - a description of the device group
234              
235             =back
236              
237             =cut
238              
239             sub devicegroup {
240             my $self = shift;
241             my $action = shift;
242             my (%args) = @_;
243              
244             if ($action eq 'set') {
245             validate(@_, { name => 1, description => 0 });
246             $args{description} //= '';
247             }
248             elsif ($action eq 'get' or $action eq 'delete') {
249             validate(@_, { name => 1 })
250             }
251             else { croak "Unknown action: $action"; }
252              
253             my $dg_name= delete $args{name};
254             my $requester = $self->_create_requester(type => 'config', action => $action);
255              
256             my $elements = $self->_generate_elements(%args);
257              
258             return $requester->(xpath => "/config/devices/entry[\@name='localhost.localdomain']/device-group/entry[\@name='$dg_name']", element => $elements);
259             }
260              
261             =head2 vsys($action, %parameters)
262              
263             Creates a new virtual system through a template
264              
265             $pano->vsys('set', template => "DG_Name", name => "VSys Name", max_sessions => 100, max_rules => 100);
266              
267             B
268              
269             =over
270              
271             =item * $action - perform an action: ['get' | 'set' | 'delete']
272              
273             =item * template - the name of the template to provision the vsys through
274              
275             =item * name - the name of the new virtual system
276              
277             =item * max_sessions - the maximum number of sessions the vsys can supprot
278              
279             =item * max_rules - the maximum number of security rules the vsys can support
280              
281             =back
282              
283             =cut
284              
285             sub vsys {
286             my $self = shift;
287             my $action = shift;
288              
289             my (%args) = @_;
290              
291             if ($action eq 'set') {
292             validate(@_, { template => 1, name => 1, max_sessions => 1, max_rules => 1 });
293             #Modify the names of the rules and session keys
294             $args{'max-sessions'} = delete $args{max_sessions};
295             $args{'max-security-rules'} = delete $args{max_rules};
296             }
297             elsif ($action eq 'get' or $action eq 'delete') {
298             validate(@_, { template => 1, name => 1})
299             }
300             else { croak "Unknown action: $action"; }
301              
302             my $template_name= delete $args{template};
303             my $vsys_name= delete $args{name};
304              
305             my $requester = $self->_create_requester(type => 'config', action => $action);
306              
307             my $elements = $self->_generate_elements(%args);
308              
309             return $requester->(xpath => "/config/devices/entry[\@name='localhost.localdomain']/template/entry[\@name='$template_name']/config/devices/entry[\@name='localhost.localdomain']/vsys/entry[\@name='$vsys_name']/import/resource",
310             element => $elements);
311             }
312              
313             =head2 subinterface($action, %parameters)
314              
315             Creates a new subinterface through a template
316              
317             $pano->subinterface('set', template => "Template_Name", parent => ethernet1/1, tag => 16, comment => 'Subinterface 16');
318              
319             B
320              
321             =over
322              
323             =item * $action - perform an action: ['get' | 'set' | 'delete']
324              
325             =item * template - the name of the template to provision the interface into
326              
327             =item * parent - the parent interface to place the sub-interface under
328              
329             =item * tag - the sub-interface ID and VLAN tag
330              
331             =item * comment - a string to describe the sub-interface
332              
333             =back
334              
335             =cut
336              
337             sub subinterface {
338             my $self = shift;
339             my $action = shift;
340             my (%args) = @_;
341              
342             if ($action eq 'set') {
343             validate(@_, { template => 1, parent => 1, tag => 1, comment => 0 });
344             }
345             elsif ($action eq 'get' or $action eq 'delete') {
346             validate(@_, { template => 1, name => 1})
347             }
348             else { croak "Unknown action: $action"; }
349              
350             my $template_name= delete $args{template};
351             my $parent = delete $args{parent};
352              
353             #We get the characters from the parent which allow us to match the parent's full name
354             my %parent_full_name = ( ae => 'aggregate-ethernet',
355             ethernet => 'ethernet',
356             );
357             my ($parent_string) = $parent =~ /[a-zA-Z]+/;
358             croak "Unkown parent interface" if undef $parent_string;
359             say $parent_string;
360            
361              
362             my $requester = $self->_create_requester(type => 'config', action => $action);
363              
364             my $elements = $self->_generate_elements(%args);
365              
366             return $requester->(xpath => "/config/devices/entry[\@name='localhost.localdomain']/template/entry[\@name='$template_name']/config/devices/entry[\@name='localhost.localdomain']/network/interface/$parent_string/entry[\@name='$parent']/layer3/units/entry[\@name='$parent.$args{tag}']",
367             element => $elements);
368             }
369              
370             =head2 virtual_router($action, %parameters)
371              
372             Creates a new virtual router through a template
373              
374             $pano->virtual_router('set', template => "Template_Name", name => "VR Name", interface => [ 'ethernet1/1', 'ethernet1/2']);
375              
376             B
377              
378             =over
379              
380             =item * $action - perform an action: ['get' | 'set' | 'delete']
381              
382             =item * template - the name of the template to provision the interface into
383              
384             =item * name - the name of the virtual router
385              
386             =item * interface - an anonymous list of interfaces to add to the virtual router
387              
388             =back
389              
390             =cut
391              
392             sub virtual_router {
393             my $self = shift;
394             my $action = shift;
395             my (%args) = @_;
396              
397             my $template_name = delete $args{template};
398             my $vr_name = delete $args{name};
399              
400             my $requester = $self->_create_requester(type => 'config', action => $action);
401              
402             my $elements = $self->_generate_elements(%args);
403              
404             return $requester->(xpath => "/config/devices/entry[\@name='localhost.localdomain']/template/entry[\@name='$template_name']/config/devices/entry[\@name='localhost.localdomain']/network/virtual-router/entry[\@name='$vr_name']",
405             element => $elements);
406             }
407              
408             =head1 OPERATIONAL COMMANDS
409              
410             =head2 commit_panorama()
411              
412             Commits the current candidate configuration on the Panorama device
413              
414             $pano->commit_panorama();
415              
416             =cut
417              
418             sub commit_panorama {
419             my $self = shift;
420              
421             my $requester = $self->_create_requester(type => 'commit');
422              
423             return $requester->(cmd => '');
424             }
425              
426              
427             =head2 commit_template
428              
429             Commit a template to the device that it is associated with
430              
431             =cut
432              
433             sub commit_template {
434             my $self = shift;
435             my %args = @_;
436              
437             validate(@_, { template => 1 });
438              
439             my $requester = $self->_create_requester(type => 'commit', action => 'all');
440              
441             return $requester->(cmd => "");
442             }
443              
444             =head2 commit_device_group
445              
446             Commit a device_group to the device that it is associated with
447              
448             =cut
449              
450             sub commit_device_group {
451             my $self = shift;
452             my %args = @_;
453              
454             validate(@_, { device_group => 1 });
455              
456             my $requester = $self->_create_requester(type => 'commit', action => 'all');
457              
458             return $requester->(cmd => "");
459             }
460             =head1 AUTHOR
461              
462             Greg Foletta, C<< >>
463              
464             =head1 BUGS
465              
466             Please report any bugs or feature requests to C, or through
467             the web interface at L. I will be notified, and then you'll
468             automatically be notified of progress on your bug as I make changes.
469              
470              
471              
472             =head1 SUPPORT
473              
474             You can find documentation for this module with the perldoc command.
475              
476             perldoc Firewall::PaloAlto
477              
478              
479             You can also look for information at:
480              
481             =over 4
482              
483             =item * RT: CPAN's request tracker (report bugs here)
484              
485             L
486              
487             =item * AnnoCPAN: Annotated CPAN documentation
488              
489             L
490              
491             =item * CPAN Ratings
492              
493             L
494              
495             =item * Search CPAN
496              
497             L
498              
499             =back
500              
501              
502             =head1 ACKNOWLEDGEMENTS
503              
504              
505             =head1 LICENSE AND COPYRIGHT
506              
507             Copyright 2015 Greg Foletta.
508              
509             This program is free software; you can redistribute it and/or modify it
510             under the terms of the the Artistic License (2.0). You may obtain a
511             copy of the full license at:
512              
513             L
514              
515             Any use, modification, and distribution of the Standard or Modified
516             Versions is governed by this Artistic License. By using, modifying or
517             distributing the Package, you accept this license. Do not use, modify,
518             or distribute the Package, if you do not accept this license.
519              
520             If your Modified Version has been derived from a Modified Version made
521             by someone other than you, you are nevertheless required to ensure that
522             your Modified Version complies with the requirements of this license.
523              
524             This license does not grant you the right to use any trademark, service
525             mark, tradename, or logo of the Copyright Holder.
526              
527             This license includes the non-exclusive, worldwide, free-of-charge
528             patent license to make, have made, use, offer to sell, sell, import and
529             otherwise transfer the Package with respect to any patent claims
530             licensable by the Copyright Holder that are necessarily infringed by the
531             Package. If you institute patent litigation (including a cross-claim or
532             counterclaim) against any party alleging that the Package constitutes
533             direct or contributory patent infringement, then this Artistic License
534             to you shall terminate on the date that such litigation is filed.
535              
536             Disclaimer of Warranty: THE PACKAGE IS PROVIDED BY THE COPYRIGHT HOLDER
537             AND CONTRIBUTORS "AS IS' AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES.
538             THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
539             PURPOSE, OR NON-INFRINGEMENT ARE DISCLAIMED TO THE EXTENT PERMITTED BY
540             YOUR LOCAL LAW. UNLESS REQUIRED BY LAW, NO COPYRIGHT HOLDER OR
541             CONTRIBUTOR WILL BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, OR
542             CONSEQUENTIAL DAMAGES ARISING IN ANY WAY OUT OF THE USE OF THE PACKAGE,
543             EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
544              
545              
546             =cut
547              
548             1; # End of Firewall::PaloAlto