1package Netdot::Model::VlanGroup;
2
3use base 'Netdot::Model';
4use warnings;
5use strict;
6
7my $logger = Netdot->log->get_logger('Netdot::Model::Device');
8
9=head1 NAME
10
11Netdot::Model::VlanGroup
12
13=head1 SYNOPSIS
14
15Netdot Vlan Group Class
16
17=head1 CLASS METHODS
18=cut
19
20####################################################################################
21
22=head2 insert - Insert new VlanGroup
23
24    Override the base class to:
25    - Validate input
26    - Assign any existing vlans to the new group if applicable
27
28  Arguments:
29    Hash ref with field/value pairs
30  Returns:
31    New VlanGroup object
32  Examples:
33    my $newgroup = VlanGroup->insert({name=>$name, start_vid=>$start, end_vid=>$end});
34
35=cut
36
37sub insert{
38    my ($class, $argv) = @_;
39    $class->isa_class_method('insert');
40
41    $class->throw_user('Vlan::insert: Argument variable must be hash reference')
42	unless ( ref($argv) eq 'HASH' );
43
44    $class->throw_user("Missing one or more required arguments: name, start_vid, end_vid")
45	unless ( exists $argv->{name} && exists $argv->{start_vid} && exists $argv->{end_vid} );
46
47    $class->_validate($argv);
48
49    my $newgroup = $class->SUPER::insert($argv);
50
51    $newgroup->assign_vlans;
52
53    return $newgroup;
54}
55
56####################################################################################
57
58=head2 assign_all - Traverse the list of vlans and assign them to groups
59
60  Arguments:
61    None
62  Returns:
63    True if successful
64  Examples:
65    VlanGroup->assign_all();
66
67=cut
68
69sub assign_all{
70    my ($class) = @_;
71    $class->isa_class_method('assign_all');
72
73    my @groups = VlanGroup->retrieve_all();
74    unless ( @groups ){
75	$logger->warn('VlanGroup::assign_all: No groups to assign vlans to');
76	return;
77    }
78
79    foreach my $group ( @groups ){
80	$group->assign_vlans;
81    }
82
83    return 1;
84}
85
86=head1 INSTANCE METHODS
87
88=cut
89####################################################################################
90
91=head2 assign_vlans - Traverse the list of vlans and assign them to this group
92
93  Arguments:
94    None
95  Returns:
96    List of member Vlans
97  Examples:
98    $group->assign_vlans();
99
100=cut
101
102sub assign_vlans{
103    my $self = shift;
104    $self->isa_object_method('assign_vlans');
105
106    # Get a list of my own vlans and index them by id
107    my %myvlans;
108    map { $myvlans{$_->id} = $_ } $self->vlans;
109
110    # Assing vlans to this new group
111    foreach my $vlan ( Vlan->retrieve_all ){
112	if ( $vlan->vid >= $self->start_vid && $vlan->vid <= $self->end_vid ){
113	    if ( !defined $vlan->vlangroup || $vlan->vlangroup != $self->id ){
114		$vlan->update({vlangroup=>$self->id});
115		$logger->debug(sub{ sprintf("VlanGroup: %s: Vlan %s within my range. Updating.",
116					    $self->name, $vlan->vid) });
117	    }
118
119	}else{
120	    # Remove from my members if necessary
121	    if ( exists $myvlans{$vlan->id} ){
122		$logger->debug(sprintf("VlanGroup %s: Vlan %s no longer within my range. Updating.",
123				      $self->name, $vlan->vid));
124		$vlan->update({vlangroup=>undef});
125	    }
126	}
127    }
128    return $self->vlans;
129}
130
131######################################################################################
132
133=head2 update - update VlanGroup objects
134
135    We override the base method to:
136    - Validate input
137    - Automatically assign Vlans to this VLAN group if it applies
138
139  Arguments:
140    hash ref with field/value pairs
141  Returns:
142    Updated VlanGroup object
143  Examples:
144
145=cut
146
147sub update{
148    my ($self, $argv) = @_;
149    $self->isa_object_method('update');
150    my $class = ref($self);
151    $self->_validate($argv);
152
153    my $oldstart = $self->start_vid;
154    my $oldend   = $self->end_vid;
155    my $id = $self->id;
156
157    my $res = $self->SUPER::update($argv);
158    $self = $class->retrieve($id);
159
160    # For some reason, we get an empty object after updating (weird)
161    # so we re-read the object from the DB to get the comparisons below to work
162    if ( $self->start_vid != $oldstart || $self->end_vid != $oldend ){
163 	my $name = $self->name;
164 	$logger->info("VlanGroup $name: Range changed.  Reassigning VLANs.");
165 	$self->assign_vlans;
166    }
167    return $res;
168}
169####################################################################################
170#
171# Private Methods
172#
173####################################################################################
174
175
176####################################################################################
177# _validate - Validate VlanGroup input before inserting or updating
178#
179#     Check that group ranges are valid and do not overlap with existing ones.
180#     Can be called as instance method or class method.
181#
182#   Arguments:
183#     Hash ref with field/value pairs
184#   Returns:
185#     True if successful
186#   Examples:
187#     VlanGroup->_validate($args);
188#
189
190sub _validate {
191    my ($proto, $argv) = @_;
192    my ($self, $class);
193    if ( $class = ref($proto) ){
194	$self = $proto;
195    }else{
196	$class = $proto;
197    }
198
199    # Get an iterator
200    my $groups = VlanGroup->retrieve_all();
201
202    # Check range validity
203    if ( $argv->{start_vid} < 1 || $argv->{end_vid} > 4096 ){
204	$class->throw_user("Invalid range: It must be within 1 and 4096");
205    }
206
207    if ( exists $argv->{start_vid} && exists $argv->{end_vid} ){
208	if ( $argv->{start_vid} > $argv->{end_vid} ){
209	    $class->throw_user("Invalid range: start must be lower or equal than end");
210	}
211	while ( my $g = $groups->next ){
212	    # Do not compare to self
213	    if ( $self && ($self->id == $g->id) ){
214		next;
215	    }
216	    # Check that ranges do not overlap
217	    unless ( ($argv->{start_vid} < $g->start_vid && $argv->{end_vid} < $g->start_vid) ||
218		     ($argv->{start_vid} > $g->end_vid && $argv->{end_vid} > $g->end_vid) ){
219
220		$class->throw_user(sprintf("New range: %d-%d overlaps with Group: %s",
221					   $argv->{start_vid}, $argv->{end_vid}, $g->name));
222	    }
223	}
224    }
225    return 1;
226}
227
228
229=head1 AUTHOR
230
231Carlos Vicente, C<< <cvicente at ns.uoregon.edu> >>
232
233=head1 COPYRIGHT & LICENSE
234
235Copyright 2012 University of Oregon, all rights reserved.
236
237This program is free software; you can redistribute it and/or modify
238it under the terms of the GNU General Public License as published by
239the Free Software Foundation; either version 2 of the License, or
240(at your option) any later version.
241
242This program is distributed in the hope that it will be useful, but
243WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTIBILITY
244or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public
245License for more details.
246
247You should have received a copy of the GNU General Public License
248along with this program; if not, write to the Free Software Foundation,
249Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
250
251=cut
252
253# Make sure to return 1
2541;
255
256