File Coverage

blib/lib/SIAM.pm
Criterion Covered Total %
statement 104 129 80.6
branch 17 32 53.1
condition 2 3 66.6
subroutine 18 20 90.0
pod 11 11 100.0
total 152 195 77.9


line stmt bran cond sub pod time code
1             package SIAM;
2              
3 2     2   98052 use warnings;
  2         5  
  2         62  
4 2     2   11 use strict;
  2         5  
  2         67  
5              
6 2     2   12 use base 'SIAM::Object';
  2         9  
  2         1151  
7              
8 2     2   1209 use SIAM::Contract;
  2         22  
  2         16  
9 2     2   1390 use SIAM::Device;
  2         4  
  2         16  
10 2     2   2027 use SIAM::User;
  2         5  
  2         18  
11 2     2   234 use SIAM::Privilege;
  2         3  
  2         9  
12 2     2   43 use SIAM::AccessScope;
  2         4  
  2         6  
13 2     2   1128 use SIAM::Attribute;
  2         5  
  2         130  
14              
15             =head1 NAME
16              
17             SIAM - Service Inventory Abstraction Model
18              
19             =cut
20              
21             our $VERSION = '1.01';
22              
23              
24             =head1 SYNOPSIS
25              
26             use SIAM;
27              
28             # Example of a SIAM configuration in a YAML file
29             ---
30             Driver:
31             Class: XYZ::SIAM::Driver
32             Options:
33             Dblink:
34             dsn: "DBI:mysql:database=xyz_inventory;host=dbhost"
35             username: siam
36             password: Lu9iifoo
37             Client:
38             Frontend:
39             enterprise.name: XYZ, Inc.
40             enterprise.url: http://example.com
41             enterprise.logo: http://example.com/logo.png
42              
43              
44             # Load the configuration from a YAML file
45             use YAML;
46             my $siamcfg = eval { YAML::LoadFile($filename) };
47             if( $@ ){
48             die("Cannot load YAML data from $filename : $@");
49             }
50              
51             # Specify your own logger object instead of the standard Log::Handler
52             $siamcfg->{'Logger'} = $logger;
53              
54             my $siam = new SIAM($siamcfg) or die('Failed loading SIAM');
55             $siam->connect() or die('Failed connecting to SIAM');
56              
57             # The monitoring system would normally need all the contracts.
58             # Walk down the hierarchy and retrieve the data for the
59             # monitoring software configuration
60              
61             my $all_contracts = $siam->get_all_contracts();
62             foreach my $contract (@{$all_contracts}) {
63             next unless $contract->is_complete();
64             my $services = $contract->get_services();
65             foreach my $service (@{$services}) {
66             next unless $service->is_complete();
67             my $units = $service->get_service_units();
68             foreach my $unit (@{$units}) {
69             next unless $unit->is_complete();
70              
71             # statistics associated with the service unit
72             my $components = $unit->get_components();
73             foreach my $c (@{$components}) {
74             next unless $c->is_complete();
75              
76             # some useful attributes for the physical unit
77             my $host = $c->attr('access.node.name');
78             my $port = $c->attr('access.port.name');
79              
80             # do something with the element attributes
81             }
82             }
83             }
84             }
85              
86             # The front-end system deals with privileges
87             my $user = $siam->get_user($uid) or return([0, 'User not found']);
88              
89             # All the contracts this user is allowed to see
90             my $contracts =
91             $siam->get_contracts_by_user_privilege($user, 'ViewContract');
92              
93             # ... walk down the hierarchy as shown above ...
94              
95             # Prepare the unit attributes for display
96             my $attrs =
97             $siam->filter_visible_attributes($user, $unit->attributes());
98              
99             # Random access to an object
100             my $el =
101             $siam->instantiate_object('SIAM::ServiceComponent', $id);
102              
103             # Check privileges on a contract
104             if( $user->has_privilege('ViewContract', $contract) ) {
105             ...
106             }
107              
108             # close the database connections
109             $siam->disconnect()
110              
111              
112             =head1 INTRODUCTION
113              
114             Many Service Provider companies (ISP, Hosting, Carriers, ...) have their
115             own, historically developed, databases for customer service
116             inventory. Therefore any system that would require access to such data
117             should be adapted to the local environment.
118              
119             SIAM is intended as a common API that would connect to
120             enterprise-specific service inventory systems and present the inventory
121             data in a uniform format. The purpose of this universal API is to reduce
122             the integration costs for such software systems as network monitoring,
123             CRM, Customer self-service portals, etc.
124              
125             We assume that monitoring systems (such as: Torrus, ...) and front-end
126             systems (such as: Customer portal, Extopus, ...) would connect to SIAM
127             to retrieve any service-specific information, and SIAM would deliver a
128             complete set of data required for those client applications.
129              
130             SIAM does not include any database of its own: all data is retrieved
131             directly from the enterprise systems and databases. The SIAM library
132             communicates with the enterprise-specific I and presents
133             the data in an abstracted way.
134              
135             SIAM takes its configuration data from a single hierarchical data
136             structure. This data is usually read from a YAML file. The
137             configuration describes all data connections, driver configuration,
138             enterprise-specific modules, etc.
139              
140             The SIAM core modules are distributed as an open-source Perl package
141             available at CPAN. Enterprise-specific modules are integrated in a way
142             that the core can be upgraded without breaking any local setup.
143              
144              
145             =head1 METHODS
146              
147             =head2 new
148              
149             Expects a hashref with SIAM configuration. Normally the calling
150             application would load the driver configuration from some data file
151             (YAML or JSON), and optionally supply its own logger object.
152              
153             The following entries are supported in the configuration:
154              
155             =over 4
156              
157             =item * Driver
158              
159             Mandatory hash with two entries: C identifying the driver module
160             class which is going to be C'd; and C, a hash which is
161             supplied to the driver's C method.
162              
163             =item * Logger
164              
165             Optional object reference that is to be used for all logging inside SIAM
166             and in the driver. The default logger is an instance of C
167             with STDERR output of warnings and errors. The logger object must
168             implement the following methods: B, B, B, and
169             B.
170              
171             =item * Client
172              
173             Optional hash that defines some configuration information for SIAM
174             clients. The keys define categories, and values point to configuration
175             hashes within each category.
176              
177             =back
178              
179             =cut
180              
181             sub new
182             {
183 1     1 1 23750 my $class = shift;
184 1         5 my $config = shift;
185              
186 1 50       7 if( defined($config->{'Logger'}) )
187             {
188 0         0 SIAM::Object->set_log_manager($config->{'Logger'});
189             }
190            
191 1         6 my $drvclass = $config->{'Driver'}{'Class'};
192 1 50       4 if( not defined($drvclass) )
193             {
194 0         0 SIAM::Object->error
195             ('Missing Driver->Class in SIAM configuration');
196 0         0 return undef;
197             }
198            
199 1         6 my $drvopts = $config->{'Driver'}{'Options'};
200 1 50       9 if( not defined($drvopts) )
201             {
202 0         0 SIAM::Object->error
203             ('Missing Driver->Options in SIAM configuration');
204 0         0 return undef;
205             }
206              
207 1         91 eval('require ' . $drvclass);
208 1 50       7 if( $@ )
209             {
210 0         0 SIAM::Object->error($@);
211 0         0 return undef;
212             }
213            
214 1         12 my $logger = SIAM::Object->get_log_manager();
215 1         5 $drvopts->{'Logger'} = $logger;
216            
217 1         67 my $driver = eval($drvclass . '->new($drvopts)');
218 1 50       6 if( $@ )
219             {
220 0         0 SIAM::Object->error($@);
221 0         0 return undef;
222             }
223            
224 1 50       5 if( not defined($driver) )
225             {
226 0         0 SIAM::Object->error('Failed to initialize the driver');
227 0         0 return undef;
228             }
229              
230 1 50       7 if( not SIAM::Object->validate_driver($driver) )
231             {
232 0         0 SIAM::Object->error('Failed to validate the driver');
233 0         0 return undef;
234             }
235              
236 1         11 my $self = $class->SUPER::new( $driver, 'SIAM.ROOT' );
237 1 50       4 return undef unless defined($self);
238              
239 1         3 my $clientconfig = $config->{'Client'};
240 1 50       3 $clientconfig = {} unless defined($clientconfig);
241 1         3 $self->{'siam_client_config'} = $clientconfig;
242              
243 1         8 return $self;
244             }
245              
246              
247             =head2 connect
248              
249             Connects the driver to its databases. Returns false in case of problems.
250              
251             =cut
252              
253             sub connect
254             {
255 1     1 1 3 my $self = shift;
256 1 50       12 if( not $self->_driver->connect() )
257             {
258 0         0 $self->error('SIAM failed to connect its driver');
259 0         0 return undef;
260             }
261              
262 1         9 return 1;
263             }
264              
265              
266             =head2 disconnect
267              
268             Disconnects the driver from its underlying databases.
269              
270             =cut
271              
272             sub disconnect
273             {
274 0     0 1 0 my $self = shift;
275 0         0 $self->_driver->disconnect();
276             }
277              
278              
279              
280             =head2 get_user
281              
282             Expects a UID string as an argument. Returns a C object or undef.
283              
284             =cut
285              
286             sub get_user
287             {
288 3     3 1 2284 my $self = shift;
289 3         7 my $uid = shift;
290              
291 3         30 my $users = $self->get_contained_objects
292             ('SIAM::User', {'match_attribute' => ['siam.user.uid', [$uid]]});
293 3 50       10 if( scalar(@{$users}) > 1 )
  3         10  
294             {
295 0         0 $self->error('Driver returned more than one SIAM::User object with ' .
296             'siam.user.uid=' . $uid);
297             }
298 3         9 return $users->[0];
299             }
300              
301              
302             =head2 get_all_contracts
303              
304             Returns an arrayref with all available C objects.
305              
306             =cut
307              
308             sub get_all_contracts
309             {
310 1     1 1 739 my $self = shift;
311 1         4 return $self->get_contained_objects('SIAM::Contract');
312             }
313              
314              
315              
316             =head2 get_contracts_by_user_privilege
317              
318             my $user_contracts =
319             $siam->get_contracts_by_user_privilege($user, 'ViewContract');
320              
321             Arguments: C object and a privilege string. Returns
322             arrayref with all available C objects that match the
323             privilege.
324              
325             =cut
326              
327             sub get_contracts_by_user_privilege
328             {
329 3     3 1 1895 my $self = shift;
330 3         6 my $user = shift;
331 3         5 my $priv = shift;
332              
333 3         13 return $user->get_objects_by_privilege($priv, 'SIAM::Contract', $self);
334             }
335            
336              
337              
338             =head2 filter_visible_attributes
339              
340             my $visible_attrs =
341             $siam->filter_visible_attributes($user, $object_attrs);
342              
343             Arguments: C object and a hashref with object attributes.
344             Returns a new hashref with copies of attributes which are allowed to be
345             shown to the user as specified by C privileges.
346              
347             =cut
348              
349             sub filter_visible_attributes
350             {
351 1     1 1 2 my $self = shift;
352 1         2 my $user = shift;
353 1         2 my $attrs_in = shift;
354              
355 1         2 my $attrs_out = {};
356              
357             # Fetch SIAM::Attribute objects only once and cache them by attribute.name
358 1 50       5 if( not defined($self->{'siam_attribute_objects'}) )
359             {
360 1         3 $self->{'siam_attribute_objects'} = {};
361 1         3 foreach my $obj (@{ $self->get_contained_objects('SIAM::Attribute') })
  1         5  
362             {
363 9         25 $self->{'siam_attribute_objects'}{$obj->name} = $obj;
364             }
365             }
366              
367 1         8 my $privileges = $user->get_contained_objects
368             ('SIAM::Privilege',
369             {'match_attribute' => ['siam.privilege.type', ['ViewAttribute']]});
370            
371 1         5 foreach my $privilege (@{$privileges})
  1         4  
372             {
373 1 50       7 if( $privilege->matches_all('SIAM::Attribute') )
374             {
375             # this user can see all. Copy everything and return.
376 0         0 while( my($key, $val) = each %{$attrs_in} )
  0         0  
377             {
378 0         0 $attrs_out->{$key} = $val;
379             }
380              
381 0         0 return $attrs_out;
382             }
383             else
384             {
385 1         3 while( my($key, $val) = each %{$attrs_in} )
  10         41  
386             {
387 9         21 my $attr_obj = $self->{'siam_attribute_objects'}{$key};
388 9 100 66     34 if( defined($attr_obj) and
389             $privilege->match_object($attr_obj) )
390             {
391 2         12 $attrs_out->{$key} = $val;
392             }
393             }
394             }
395             }
396              
397 1         7 return $attrs_out;
398             }
399              
400              
401             =head2 get_all_devices
402              
403             Returns an arrayref with all available C objects.
404              
405             =cut
406              
407             sub get_all_devices
408             {
409 0     0 1 0 my $self = shift;
410 0         0 return $self->get_contained_objects('SIAM::Device');
411             }
412              
413              
414             =head2 get_device
415              
416             Takes the device inventory ID and returns an C object.
417              
418             =cut
419              
420             sub get_device
421             {
422 1     1 1 1253 my $self = shift;
423 1         3 my $invid = shift;
424            
425 1         12 my $devices = $self->get_contained_objects
426             ('SIAM::Device',
427             {'match_attribute' => ['siam.device.inventory_id', [$invid]]});
428            
429 1 50       4 if( scalar(@{$devices}) > 1 )
  1         6  
430             {
431 0         0 $self->error('Driver returned more than one SIAM::Device object ' .
432             ' with siam.device.inventory_id=' . $invid);
433             }
434 1         4 return $devices->[0];
435             }
436              
437              
438              
439             =head2 get_client_config
440              
441             Takes the category name and returns a hashref with I
442             configuration for the specified category. Returns an empty hashref if
443             the configuration is not available.
444              
445             =cut
446              
447             sub get_client_config
448             {
449 1     1 1 3 my $self = shift;
450 1         3 my $category = shift;
451            
452 1         4 my $ret = $self->{'siam_client_config'}{$category};
453 1 50       3 $ret = {} unless defined($ret);
454 1         7 return $ret;
455             }
456              
457              
458             =head2 manifest_attributes
459              
460             The method returns an arrayref with all known attrubute names that are
461             supported by SIAM internal modules and the driver.
462              
463             =cut
464              
465             sub manifest_attributes
466             {
467 1     1 1 281374 my $self = shift;
468              
469 1         5 my $ret = ['siam.object.id', 'siam.object.class'];
470 1         5 foreach my $class ('SIAM::User', 'SIAM::Contract', 'SIAM::Attribute',
471             'SIAM::AccessScope', 'SIAM::Device')
472             {
473 5         7 push(@{$ret}, @{ $class->_manifest_attributes() });
  5         7  
  5         46  
474             }
475              
476 1         2 push(@{$ret}, @{ $self->_driver->manifest_attributes() });
  1         4  
  1         7  
477 1         5 return [sort @{$ret}];
  1         29  
478             }
479              
480              
481              
482             =head1 SEE ALSO
483              
484             L, L
485              
486              
487             =head1 AUTHOR
488              
489             Stanislav Sinyagin, C<< >>
490              
491             =head1 BUGS
492              
493             Please report any bugs or feature requests to C
494             rt.cpan.org>, or through the web interface at
495             L. I will be
496             notified, and then you'll automatically be notified of progress on your
497             bug as I make changes.
498              
499              
500              
501              
502             =head1 SUPPORT
503              
504             You can find documentation for this module with the perldoc command.
505              
506             perldoc SIAM
507              
508              
509             You can also look for information at:
510              
511             =over 4
512              
513             =item * RT: CPAN's request tracker
514              
515             L
516              
517             =item * AnnoCPAN: Annotated CPAN documentation
518              
519             L
520              
521             =item * CPAN Ratings
522              
523             L
524              
525             =item * Search CPAN
526              
527             L
528              
529             =back
530              
531              
532              
533             =head1 LICENSE AND COPYRIGHT
534              
535             Copyright 2011 Stanislav Sinyagin.
536              
537             This program is distributed under the MIT (X11) License:
538             L
539              
540             Permission is hereby granted, free of charge, to any person
541             obtaining a copy of this software and associated documentation
542             files (the "Software"), to deal in the Software without
543             restriction, including without limitation the rights to use,
544             copy, modify, merge, publish, distribute, sublicense, and/or sell
545             copies of the Software, and to permit persons to whom the
546             Software is furnished to do so, subject to the following
547             conditions:
548              
549             The above copyright notice and this permission notice shall be
550             included in all copies or substantial portions of the Software.
551              
552             THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
553             EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
554             OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
555             NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
556             HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
557             WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
558             FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
559             OTHER DEALINGS IN THE SOFTWARE.
560              
561              
562             =cut
563              
564             1;
565              
566             # Local Variables:
567             # mode: cperl
568             # indent-tabs-mode: nil
569             # cperl-indent-level: 4
570             # cperl-continued-statement-offset: 4
571             # cperl-continued-brace-offset: -4
572             # cperl-brace-offset: 0
573             # cperl-label-offset: -2
574             # End: