Some cosmetic edits
[clearscm.git] / lib / Machines / MySQL.pm
1 =pod\r
2 \r
3 =head1 NAME $RCSfile: MySQL.pm,v $\r
4 \r
5 MySQL Backend for Machines module\r
6 \r
7 =head1 VERSION\r
8 \r
9 =over\r
10 \r
11 =item Author\r
12 \r
13 Andrew DeFaria <Andrew@DeFaria.com>\r
14 \r
15 =item Revision\r
16 \r
17 $Revision: $\r
18 \r
19 =item Created\r
20 \r
21 Mon, Jul 16, 2018 10:13:12 AM\r
22 \r
23 =item Modified\r
24 \r
25 $Date: $\r
26 \r
27 =back\r
28 \r
29 =head1 SYNOPSIS\r
30 \r
31 Interfaces to a MySQL backend for machine information\r
32 \r
33 =head1 DESCRIPTION\r
34 \r
35 The rexec.pl script allows you to execute an arbitrary command on a set of\r
36 machines, however what set of machines? Primative exeuction involves just a\r
37 flat file with machine information listed in it. This module instead provides\r
38 a MySQL backend for this machine data.\r
39 \r
40 =head1 ROUTINES\r
41 \r
42 The following methods are available:\r
43 \r
44 =cut\r
45 \r
46 package Machines::MySQL;\r
47 \r
48 use strict;\r
49 use warnings;\r
50 \r
51 use Carp;\r
52 use DBI;\r
53 \r
54 use parent qw(Machines);\r
55 \r
56 our $VERSION  = '$Revision: 1.0 $';\r
57    ($VERSION) = ($VERSION =~ /\$Revision: (.*) /);\r
58 \r
59 my %MACHINEOPTS = (\r
60   SERVER   => 'localhost',\r
61   USERNAME => 'machines',\r
62   PASSWORD => 'w0rk$harder',\r
63 );\r
64 \r
65 sub _connect (;$) {\r
66   my ($self, $dbserver) = @_;\r
67 \r
68   $dbserver ||= $MACHINEOPTS{SERVER};\r
69 \r
70   my $dbname   = 'machines';\r
71   my $dbdriver = 'mysql';\r
72 \r
73   $self->{db} = DBI->connect (\r
74     "DBI:$dbdriver:$dbname:$dbserver",\r
75     $MACHINEOPTS{USERNAME},\r
76     $MACHINEOPTS{PASSWORD},\r
77     {PrintError => 0},\r
78   ) or croak (\r
79     "Couldn't connect to $dbname database "\r
80   . "as $MACHINEOPTS{USERNAME}\@$MACHINEOPTS{SERVER}"\r
81   );\r
82 \r
83   $self->{dbserver} = $dbserver;\r
84 \r
85   return;\r
86 } # _connect\r
87 \r
88 sub _checkRequiredFields ($$) {\r
89   my ($fields, $rec) = @_;\r
90 \r
91   for my $fieldname (@$fields) {\r
92     my $found = 0;\r
93 \r
94     for (keys %$rec) {\r
95       if ($fieldname eq $_) {\r
96          $found = 1;\r
97          last;\r
98       } # if\r
99     } # for\r
100 \r
101     return "$fieldname is required"\r
102       unless $found;\r
103   } # for\r
104 \r
105   return;\r
106 } # _checkRequiredFields\r
107 \r
108 # Internal methods\r
109 sub _dberror ($$) {\r
110   my ($self, $msg, $statement) = @_;\r
111 \r
112   my $dberr    = $self->{db}->err;\r
113   my $dberrmsg = $self->{db}->errstr;\r
114 \r
115   $dberr    ||= 0;\r
116   $dberrmsg ||= 'Success';\r
117 \r
118   my $message = '';\r
119 \r
120   if ($dberr) {\r
121     my $function = (caller (1)) [3];\r
122 \r
123     $message = "$function: $msg\nError #$dberr: $dberrmsg\n"\r
124              . "SQL Statement: $statement";\r
125   } # if\r
126 \r
127   return $dberr, $message;\r
128 } # _dberror\r
129 \r
130 sub _formatValues (@) {\r
131   my ($self, @values) = @_;\r
132 \r
133   my @returnValues;\r
134 \r
135   # Quote data values\r
136   push @returnValues, $_ eq '' ? 'null' : $self->{db}->quote ($_)\r
137     for (@values);\r
138 \r
139   return @returnValues;\r
140 } # _formatValues\r
141 \r
142 sub _formatNameValues (%) {\r
143   my ($self, %rec) = @_;\r
144 \r
145   my @nameValueStrs;\r
146 \r
147   push @nameValueStrs, "$_=" . $self->{db}->quote ($rec{$_})\r
148     for (keys %rec);\r
149 \r
150   return @nameValueStrs;\r
151 } # _formatNameValues\r
152 \r
153 sub _addRecord ($%) {\r
154   my ($self, $table, %rec) = @_;\r
155 \r
156   my $statement  = "insert into $table (";\r
157      $statement .= join ',', keys %rec;\r
158      $statement .= ') values (';\r
159      $statement .= join ',', $self->_formatValues (values %rec);\r
160      $statement .= ')';\r
161 \r
162   my ($err, $msg);\r
163 \r
164   $self->{db}->do ($statement);\r
165 \r
166   return $self->_dberror ("Unable to add record to $table", $statement);\r
167 } # _addRecord\r
168 \r
169 sub _getRecords ($;$) {\r
170   my ($self, $table, $condition) = @_;\r
171 \r
172   my ($err, $msg);\r
173 \r
174   my $statement  = "select * from $table";\r
175 \r
176   if ($condition) {\r
177     $condition .= ' and ';\r
178   } # if\r
179 \r
180   $condition .= 'active = "true"';\r
181   $statement .= " where $condition";\r
182 \r
183   my $sth = $self->{db}->prepare($statement);\r
184 \r
185   unless ($sth) {\r
186     ($err, $msg) = $self->_dberror('Unable to prepare statement', $statement);\r
187 \r
188     croak $msg;\r
189   } # if\r
190 \r
191   my $status = $sth->execute;\r
192 \r
193   ($err, $msg) = $self->_dberror ('Unable to execute statement', $statement);\r
194 \r
195   return ($err, $msg) if $err;\r
196 \r
197   my %records;\r
198 \r
199   while (my $row = $sth->fetchrow_hashref) {\r
200     # Change undef to ''\r
201     $row->{$_} ||= '' for keys %$row;\r
202 \r
203     my $name = delete $row->{name};\r
204 \r
205     $records{$name} = $row;\r
206   } # while\r
207 \r
208   return %records;\r
209 } # _getRecord\r
210 \r
211 sub new (;$) {\r
212   my ($class, $db) = @_;\r
213 \r
214   my $self = bless {}, $class;\r
215 \r
216   $self->_connect ($db);\r
217 \r
218   return $self;\r
219 } # new\r
220 \r
221 sub select(;$) {\r
222   my ($self, $condition) = @_;\r
223 \r
224   return $self->_getRecords('system', $condition);\r
225 } # select\r
226 \r
227 sub AddSystem (%) {\r
228   my ($self, %system) = @_;\r
229 \r
230   my @requiredFields = (\r
231     'name',\r
232     'type',\r
233   );\r
234 \r
235   my $result = _checkRequiredFields \@requiredFields, \%system;\r
236 \r
237   return -1, "AddSystem: $result" if $result;\r
238 \r
239   return $self->_addRecord ('system', %system);\r
240 } # AddSystem\r
241 \r
242 1;