| 1 | # Copyright (C) 2007 Insecure.Com LLC. |
|---|
| 2 | # |
|---|
| 3 | # Author: Guilherme Polo <ggpolo@gmail.com> |
|---|
| 4 | # |
|---|
| 5 | # This program is free software; you can redistribute it and/or modify |
|---|
| 6 | # it under the terms of the GNU General Public License as published by |
|---|
| 7 | # the Free Software Foundation; either version 2 of the License, or |
|---|
| 8 | # (at your option) any later version. |
|---|
| 9 | # |
|---|
| 10 | # This program is distributed in the hope that it will be useful, |
|---|
| 11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of |
|---|
| 12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|---|
| 13 | # GNU General Public License for more details. |
|---|
| 14 | # |
|---|
| 15 | # You should have received a copy of the GNU General Public License |
|---|
| 16 | # along with this program; if not, write to the Free Software |
|---|
| 17 | # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 |
|---|
| 18 | # USA |
|---|
| 19 | |
|---|
| 20 | from umitCore.I18N import _ |
|---|
| 21 | |
|---|
| 22 | from umitDB.Store import RawStore |
|---|
| 23 | from umitDB.Retrieve import InventoryRetrieve |
|---|
| 24 | |
|---|
| 25 | class BadParams(Exception): |
|---|
| 26 | pass |
|---|
| 27 | |
|---|
| 28 | class UpdateChanges(InventoryRetrieve, RawStore): |
|---|
| 29 | """ |
|---|
| 30 | Updates list of changes for a given Inventory id. |
|---|
| 31 | """ |
|---|
| 32 | |
|---|
| 33 | def __init__(self, database, fk_inventory=None): |
|---|
| 34 | |
|---|
| 35 | InventoryRetrieve.__init__(self, database) |
|---|
| 36 | RawStore.__init__(self, self.conn, self.cursor) |
|---|
| 37 | self.fk_inventory = fk_inventory |
|---|
| 38 | |
|---|
| 39 | if fk_inventory: |
|---|
| 40 | self.do_update(fk_inventory) |
|---|
| 41 | |
|---|
| 42 | |
|---|
| 43 | def do_update(self, inv_id): |
|---|
| 44 | """ |
|---|
| 45 | Build changes found in scans related to an Inventory id and |
|---|
| 46 | updates everything necessary. |
|---|
| 47 | """ |
|---|
| 48 | addresses = [ ] |
|---|
| 49 | |
|---|
| 50 | # get scan ids and finish time for especified inventory |
|---|
| 51 | finish_data = self.get_finish_data_for_inventory_from_db(inv_id) |
|---|
| 52 | |
|---|
| 53 | |
|---|
| 54 | # retrieve host addresses for especified inventory id |
|---|
| 55 | for scan in finish_data: |
|---|
| 56 | # retrieve host ids for each scan |
|---|
| 57 | for host in self.get_hosts_id_for_scan_from_db(scan[0]): |
|---|
| 58 | # retrieve ipv4 address for host |
|---|
| 59 | addr = self.get_ipv4_for_host_from_db(host[0]) |
|---|
| 60 | |
|---|
| 61 | if addr in addresses: |
|---|
| 62 | continue |
|---|
| 63 | |
|---|
| 64 | addresses.append(addr) |
|---|
| 65 | |
|---|
| 66 | |
|---|
| 67 | # generate changes list for each address in especified inventory |
|---|
| 68 | for addr in addresses: |
|---|
| 69 | |
|---|
| 70 | # get all host pks for especified inventory and for each address |
|---|
| 71 | # in addresses |
|---|
| 72 | base_data = self.get_hosts_base_data_for_inventory_from_db(addr, |
|---|
| 73 | inv_id) |
|---|
| 74 | |
|---|
| 75 | if not base_data: |
|---|
| 76 | print "Inventory id %d has no data for host \ |
|---|
| 77 | address '%s' yet." % (inv_id, addr) |
|---|
| 78 | continue |
|---|
| 79 | |
|---|
| 80 | # dict where all data will be stored |
|---|
| 81 | data_dict = { } |
|---|
| 82 | |
|---|
| 83 | # get host and scan ids |
|---|
| 84 | scan_ids = [item[0] for item in finish_data] |
|---|
| 85 | host_ids = [item[0] for item in base_data] |
|---|
| 86 | |
|---|
| 87 | indexes = [ ] |
|---|
| 88 | count = 0 |
|---|
| 89 | # get indexes where there is host data |
|---|
| 90 | for indx, item in enumerate(scan_ids): |
|---|
| 91 | try: |
|---|
| 92 | host_ids[count] |
|---|
| 93 | except IndexError: |
|---|
| 94 | continue |
|---|
| 95 | else: |
|---|
| 96 | indexes.append(scan_ids.index(host_ids[count])) |
|---|
| 97 | count += 1 |
|---|
| 98 | |
|---|
| 99 | down_time = self._check_for_downtime(finish_data, base_data) |
|---|
| 100 | for item in down_time[1]: |
|---|
| 101 | data_dict[item] = (_("Availability"), _("Host down"), |
|---|
| 102 | down_time[0][item], -1, -1) |
|---|
| 103 | |
|---|
| 104 | # load initial data for doing comparison |
|---|
| 105 | date = finish_data[indexes[0]][1] |
|---|
| 106 | hostA = base_data[0] |
|---|
| 107 | self.use_dict_cursor() |
|---|
| 108 | pdata1 = self.get_portid_and_state_for_host_from_db(hostA[1]) |
|---|
| 109 | self.use_standard_cursor() |
|---|
| 110 | fpinfo1 = self.get_fingerprint_info_for_host_from_db(hostA[1]) |
|---|
| 111 | osmatch1 = self.get_osmatch_for_host_from_db(hostA[1]) |
|---|
| 112 | osclasses1 = self.get_osclasses_for_host_from_db(hostA[1]) |
|---|
| 113 | pcdata1 = self.get_portid_and_fks_for_host_from_db(hostA[1]) |
|---|
| 114 | |
|---|
| 115 | host_count = 1 |
|---|
| 116 | |
|---|
| 117 | # now, load following data and compare against hostA |
|---|
| 118 | for indx, item in enumerate(base_data[host_count:]): |
|---|
| 119 | hostB = item |
|---|
| 120 | |
|---|
| 121 | # load data to compare against hostA |
|---|
| 122 | self.use_dict_cursor() |
|---|
| 123 | pdata2 = self.get_portid_and_state_for_host_from_db(hostB[1]) |
|---|
| 124 | self.use_standard_cursor() |
|---|
| 125 | fpinfo2 = self.get_fingerprint_info_for_host_from_db(hostB[1]) |
|---|
| 126 | osmatch2 = self.get_osmatch_for_host_from_db(hostB[1]) |
|---|
| 127 | osclasses2 = self.get_osclasses_for_host_from_db(hostB[1]) |
|---|
| 128 | pcdata2 = self.get_portid_and_fks_for_host_from_db(hostB[1]) |
|---|
| 129 | |
|---|
| 130 | # compare old data against new data |
|---|
| 131 | category, diff_text = self._compare_data(pdata2, fpinfo2, |
|---|
| 132 | osmatch2, osclasses2, |
|---|
| 133 | pcdata2, |
|---|
| 134 | pdata1, fpinfo1, |
|---|
| 135 | osmatch1, osclasses1, |
|---|
| 136 | pcdata1) |
|---|
| 137 | |
|---|
| 138 | # store in data_dict current result |
|---|
| 139 | data_dict[hostA[0]] = (category, diff_text, date, hostA[1], |
|---|
| 140 | hostB[1]) |
|---|
| 141 | |
|---|
| 142 | # swap data |
|---|
| 143 | pdata1 = pdata2 |
|---|
| 144 | pcdata1 = pcdata2 |
|---|
| 145 | fpinfo1 = fpinfo2 |
|---|
| 146 | osmatch1 = osmatch2 |
|---|
| 147 | osclasses1 = osclasses2 |
|---|
| 148 | hostA = hostB |
|---|
| 149 | |
|---|
| 150 | # get next date |
|---|
| 151 | date = finish_data[indexes[indx+1]][1] |
|---|
| 152 | |
|---|
| 153 | # now load the first entry |
|---|
| 154 | data_dict[hostA[0]] = (_("Inventory"), |
|---|
| 155 | _("Host added to the Inventory."), date, |
|---|
| 156 | hostA[1], hostA[1]) |
|---|
| 157 | |
|---|
| 158 | self._insert_changes(data_dict, addr, inv_id) |
|---|
| 159 | |
|---|
| 160 | |
|---|
| 161 | def _insert_changes(self, data_dict, addr_id, inventory_id): |
|---|
| 162 | """ |
|---|
| 163 | Insert changes in database, this was collected at do_update method. |
|---|
| 164 | """ |
|---|
| 165 | # sort dict keys in descendent order |
|---|
| 166 | dict_keys = data_dict.keys() |
|---|
| 167 | dict_keys.sort() |
|---|
| 168 | dict_keys.reverse() |
|---|
| 169 | |
|---|
| 170 | # insert data into database |
|---|
| 171 | fk_address = self.get_address_id_for_address_from_db(addr_id) |
|---|
| 172 | for key in dict_keys: |
|---|
| 173 | affected = data_dict[key][0] |
|---|
| 174 | text = data_dict[key][1] |
|---|
| 175 | date = data_dict[key][2] |
|---|
| 176 | new_hostid = data_dict[key][3] |
|---|
| 177 | old_hostid = data_dict[key][4] |
|---|
| 178 | |
|---|
| 179 | # check if category 'affected' already exists on database |
|---|
| 180 | fk_category = self.get_inventory_change_category_id(affected) |
|---|
| 181 | if not fk_category: |
|---|
| 182 | # didn't exist, create it now |
|---|
| 183 | self.insert_inventory_change_category_db(affected) |
|---|
| 184 | fk_category = self.get_id_for("inventory_change_category") |
|---|
| 185 | |
|---|
| 186 | # check if comparison is already in database |
|---|
| 187 | # (this should have been done at earlier stage, but for now |
|---|
| 188 | # it is being done here) |
|---|
| 189 | ret = self.get_inventory_comparison(old_hostid, new_hostid, |
|---|
| 190 | date, inventory_id) |
|---|
| 191 | |
|---|
| 192 | if not ret: |
|---|
| 193 | # need to insert new comparison |
|---|
| 194 | self.insert_inventory_comparison_db(old_hostid, new_hostid, |
|---|
| 195 | date, text, inventory_id, |
|---|
| 196 | fk_category, fk_address) |
|---|
| 197 | |
|---|
| 198 | |
|---|
| 199 | def _compare_data(self, pdata2, fpinfo2, osmatch2, osclasses2, pcdata2, |
|---|
| 200 | pdata1, fpinfo1, osmatch1, osclasses1, pcdata1): |
|---|
| 201 | """ |
|---|
| 202 | Compare two sets of data, checking how it changes. |
|---|
| 203 | """ |
|---|
| 204 | host_diff = '' |
|---|
| 205 | common_text = '' |
|---|
| 206 | ports_only = None # ports diff only |
|---|
| 207 | fp_only = None # fingerprint diff only |
|---|
| 208 | |
|---|
| 209 | # compare pdataNs |
|---|
| 210 | if pdata1 != pdata2: |
|---|
| 211 | host_diff += self._ports_diff(pdata2, pdata1) |
|---|
| 212 | fp_only = False |
|---|
| 213 | ports_only = True |
|---|
| 214 | |
|---|
| 215 | # compare pcdataNs (pc here stands for port complete, wich means |
|---|
| 216 | # we will be dealing with other port infos beyond id and states, |
|---|
| 217 | # like we did right above. |
|---|
| 218 | old_ports = { } |
|---|
| 219 | new_ports = { } |
|---|
| 220 | |
|---|
| 221 | self.use_dict_cursor() |
|---|
| 222 | for pd in pcdata2: |
|---|
| 223 | fpd = self.get_port_data_for_pdata_from_db(pd[2], pd[3], pd[1]) |
|---|
| 224 | old_ports[pd[0]] = fpd |
|---|
| 225 | |
|---|
| 226 | for pd in pcdata1: |
|---|
| 227 | fpd = self.get_port_data_for_pdata_from_db(pd[2], pd[3], pd[1]) |
|---|
| 228 | new_ports[pd[0]] = fpd |
|---|
| 229 | self.use_standard_cursor() |
|---|
| 230 | |
|---|
| 231 | info_changes = [ ] |
|---|
| 232 | for key, value in old_ports.items(): |
|---|
| 233 | if key in new_ports: |
|---|
| 234 | if value != new_ports[key]: |
|---|
| 235 | info_changes.append(key) |
|---|
| 236 | |
|---|
| 237 | if info_changes: |
|---|
| 238 | if not ports_only: |
|---|
| 239 | ports_only = True |
|---|
| 240 | |
|---|
| 241 | verb, plural = self.conjugate(info_changes, False) |
|---|
| 242 | ports_str = ', '.join([str(p) for p in info_changes]) |
|---|
| 243 | host_diff += _("Port%s %s %s changed info!" % (plural, ports_str, |
|---|
| 244 | verb)) |
|---|
| 245 | |
|---|
| 246 | # compare fpinfoNs |
|---|
| 247 | # dont consider uptime and lastboot in fingerprint (will probably |
|---|
| 248 | # not consider others too) |
|---|
| 249 | if fpinfo1 and (fpinfo1[2:] != fpinfo2[2:]): |
|---|
| 250 | space = host_diff and ' ' or '' |
|---|
| 251 | common_text = _('%sFingerprint, ') % space |
|---|
| 252 | ports_only = False |
|---|
| 253 | if fp_only is None: |
|---|
| 254 | fp_only = True |
|---|
| 255 | |
|---|
| 256 | # compare osmatchNs |
|---|
| 257 | if osmatch1 != osmatch2: |
|---|
| 258 | space = host_diff and ' ' or '' |
|---|
| 259 | common_text += _("OS Match, ") |
|---|
| 260 | fp_only = False |
|---|
| 261 | ports_only = False |
|---|
| 262 | |
|---|
| 263 | # compare osclassesNs |
|---|
| 264 | if osclasses1 != osclasses2: |
|---|
| 265 | space = host_diff and ' ' or '' |
|---|
| 266 | if len(common_text) == len("Fingerprint, "): |
|---|
| 267 | common_text = common_text[:-2] + " " |
|---|
| 268 | if common_text: |
|---|
| 269 | common_text += _("and OS Classes") |
|---|
| 270 | else: |
|---|
| 271 | common_text += _("OS Classes") |
|---|
| 272 | fp_only = False |
|---|
| 273 | ports_only = False |
|---|
| 274 | |
|---|
| 275 | if common_text: |
|---|
| 276 | if fp_only or len(common_text) == len(" Fingerprint, "): |
|---|
| 277 | common_text = common_text[:-2] |
|---|
| 278 | host_diff += common_text + _(" changed.") |
|---|
| 279 | |
|---|
| 280 | # check diff |
|---|
| 281 | if host_diff: |
|---|
| 282 | if ports_only: |
|---|
| 283 | affected = _("Ports") |
|---|
| 284 | elif fp_only: |
|---|
| 285 | affected = _("Fingerprint") |
|---|
| 286 | else: |
|---|
| 287 | affected = _("Several") |
|---|
| 288 | |
|---|
| 289 | else: |
|---|
| 290 | # Nothing here means "Almost nothing", there could be |
|---|
| 291 | # changes in extraports for example. |
|---|
| 292 | affected = _("Nothing") |
|---|
| 293 | host_diff = _("No noticeables changes since last sucessfull scan.") |
|---|
| 294 | |
|---|
| 295 | |
|---|
| 296 | return (affected, host_diff) |
|---|
| 297 | |
|---|
| 298 | |
|---|
| 299 | def conjugate(self, alist, toBe=True): |
|---|
| 300 | """ |
|---|
| 301 | Do conjugation based on alist size. |
|---|
| 302 | It expects that alist is not empty. |
|---|
| 303 | """ |
|---|
| 304 | if toBe: |
|---|
| 305 | verbs = ('are', 'is') |
|---|
| 306 | else: |
|---|
| 307 | verbs = ('have', 'has') |
|---|
| 308 | |
|---|
| 309 | if len(alist) > 1: |
|---|
| 310 | verb = verbs[0] |
|---|
| 311 | plural = _('s') |
|---|
| 312 | else: |
|---|
| 313 | verb = verbs[1] |
|---|
| 314 | plural = _('') |
|---|
| 315 | |
|---|
| 316 | return verb, plural |
|---|
| 317 | |
|---|
| 318 | |
|---|
| 319 | def _ports_diff(self, old, new): |
|---|
| 320 | """ |
|---|
| 321 | Return a prettier difference between pdata. |
|---|
| 322 | """ |
|---|
| 323 | closed_text = _("closed") |
|---|
| 324 | open_text = _("open") |
|---|
| 325 | now_text = _("now") |
|---|
| 326 | port_text = _("Port") |
|---|
| 327 | and_text = _("and") |
|---|
| 328 | |
|---|
| 329 | # first build dict where portid is the key |
|---|
| 330 | old_dict = { } |
|---|
| 331 | new_dict = { } |
|---|
| 332 | |
|---|
| 333 | for d in old: |
|---|
| 334 | old_dict[d['portid']] = d['state'] |
|---|
| 335 | |
|---|
| 336 | for d in new: |
|---|
| 337 | new_dict[d['portid']] = d['state'] |
|---|
| 338 | |
|---|
| 339 | # check for port changes now |
|---|
| 340 | closed_ports = [ ] |
|---|
| 341 | open_ports = [ ] |
|---|
| 342 | |
|---|
| 343 | for key, value in old_dict.items(): |
|---|
| 344 | if key in new_dict: |
|---|
| 345 | new_value = new_dict[key] |
|---|
| 346 | |
|---|
| 347 | if value != new_value: |
|---|
| 348 | print value, new_value, 'differs but Im not doing nothing' |
|---|
| 349 | |
|---|
| 350 | else: |
|---|
| 351 | closed_ports.append(key) |
|---|
| 352 | |
|---|
| 353 | for key, value in new_dict.items(): |
|---|
| 354 | if key in old_dict: |
|---|
| 355 | old_value = old_dict[key] |
|---|
| 356 | |
|---|
| 357 | if value != old_value: |
|---|
| 358 | print value, old_value, 'differs but Im not doing nothing' |
|---|
| 359 | |
|---|
| 360 | else: |
|---|
| 361 | open_ports.append(key) |
|---|
| 362 | |
|---|
| 363 | text = '' |
|---|
| 364 | if closed_ports: |
|---|
| 365 | verb, plural = self.conjugate(closed_ports) |
|---|
| 366 | closed_ports = ', '.join([str(p) for p in closed_ports]) |
|---|
| 367 | closed_ports = "%s%s %s %s %s %s." % (port_text, plural, |
|---|
| 368 | closed_ports, verb, |
|---|
| 369 | closed_text, now_text) |
|---|
| 370 | |
|---|
| 371 | if open_ports: |
|---|
| 372 | verb, plural = self.conjugate(open_ports) |
|---|
| 373 | open_ports = ', '.join([str(p) for p in open_ports]) |
|---|
| 374 | open_ports = "%s%s %s %s %s %s." % (port_text, plural, |
|---|
| 375 | open_ports, verb, |
|---|
| 376 | open_text, now_text) |
|---|
| 377 | |
|---|
| 378 | if open_ports and closed_ports: |
|---|
| 379 | text = open_ports[:-1 -len(now_text) -1] + ' ' + and_text + \ |
|---|
| 380 | ' ' + closed_ports |
|---|
| 381 | elif open_ports: |
|---|
| 382 | text = open_ports |
|---|
| 383 | elif closed_ports: |
|---|
| 384 | text = closed_ports |
|---|
| 385 | |
|---|
| 386 | return text |
|---|
| 387 | |
|---|
| 388 | |
|---|
| 389 | def _check_for_downtime(self, all_scans, host_scans): |
|---|
| 390 | """ |
|---|
| 391 | Return scan id associated to a date showing in what scans a host |
|---|
| 392 | was down. |
|---|
| 393 | """ |
|---|
| 394 | down_d = { } |
|---|
| 395 | down_order = [ ] |
|---|
| 396 | d = 0 |
|---|
| 397 | for item in all_scans: |
|---|
| 398 | try: |
|---|
| 399 | host_scans[d][0] |
|---|
| 400 | except IndexError: |
|---|
| 401 | down_order.append(item[0]) |
|---|
| 402 | down_d[item[0]] = item[1] |
|---|
| 403 | else: |
|---|
| 404 | if item[0] == host_scans[d][0]: |
|---|
| 405 | d += 1 |
|---|
| 406 | else: |
|---|
| 407 | down_order.append(item[0]) |
|---|
| 408 | down_d[item[0]] = item[1] |
|---|
| 409 | |
|---|
| 410 | return (down_d, down_order) |
|---|
| 411 | |
|---|
| 412 | |
|---|
| 413 | class ChangesRetrieve(InventoryRetrieve): |
|---|
| 414 | """ |
|---|
| 415 | Retrieves changes from database in many ways. |
|---|
| 416 | """ |
|---|
| 417 | |
|---|
| 418 | def __init__(self, database): |
|---|
| 419 | InventoryRetrieve.__init__(self, database) |
|---|
| 420 | |
|---|
| 421 | |
|---|
| 422 | def get_categories_id_name(self): |
|---|
| 423 | """ |
|---|
| 424 | Return all category_id, category_name from database. |
|---|
| 425 | """ |
|---|
| 426 | self.cursor.execute("SELECT * FROM inventory_change_category") |
|---|
| 427 | data = self.cursor.fetchall() |
|---|
| 428 | |
|---|
| 429 | return data |
|---|
| 430 | |
|---|
| 431 | |
|---|
| 432 | def get_categories_name(self): |
|---|
| 433 | """ |
|---|
| 434 | Return all categories name from database. |
|---|
| 435 | """ |
|---|
| 436 | self.cursor.execute("SELECT name FROM inventory_change_category") |
|---|
| 437 | ctg = self.cursor.fetchall() |
|---|
| 438 | |
|---|
| 439 | return ctg |
|---|
| 440 | |
|---|
| 441 | |
|---|
| 442 | def get_category_name_by_id(self, cid): |
|---|
| 443 | """ |
|---|
| 444 | Return category name with especified id. |
|---|
| 445 | """ |
|---|
| 446 | name = self.cursor.execute("SELECT name FROM inventory_change_category\ |
|---|
| 447 | WHERE pk=?", (cid, )).fetchone() |
|---|
| 448 | |
|---|
| 449 | if name: |
|---|
| 450 | return name[0] |
|---|
| 451 | |
|---|
| 452 | |
|---|
| 453 | def get_category_id_by_name(self, name): |
|---|
| 454 | """ |
|---|
| 455 | Return category id with especified name. |
|---|
| 456 | """ |
|---|
| 457 | cid = self.cursor.execute("SELECT pk FROM inventory_change_category \ |
|---|
| 458 | WHERE name=?", (name, )).fetchone()[0] |
|---|
| 459 | |
|---|
| 460 | return cid |
|---|
| 461 | |
|---|
| 462 | |
|---|
| 463 | def timerange_changes_data_generic(self, start, end, category, inventory, |
|---|
| 464 | hostaddr): |
|---|
| 465 | """ |
|---|
| 466 | Selects what method to use to grab changes data. |
|---|
| 467 | """ |
|---|
| 468 | # start and end should always be present |
|---|
| 469 | if not start or not end: |
|---|
| 470 | raise BadParams("You should especify range start and range end") |
|---|
| 471 | |
|---|
| 472 | # check for missing args for inventory and hostaddr |
|---|
| 473 | if inventory and not hostaddr or not inventory and hostaddr: |
|---|
| 474 | raise BadParams("You should especify a hostaddr and inventory") |
|---|
| 475 | |
|---|
| 476 | |
|---|
| 477 | if category: |
|---|
| 478 | return self.timerange_changes_categoryid_data(category, start, |
|---|
| 479 | end, inventory, |
|---|
| 480 | hostaddr) |
|---|
| 481 | else: |
|---|
| 482 | return self.timerange_changes_data(start, end, inventory, |
|---|
| 483 | hostaddr) |
|---|
| 484 | |
|---|
| 485 | |
|---|
| 486 | def timerange_changes_data(self, start, end, fk_inventory=None, |
|---|
| 487 | fk_address=None): |
|---|
| 488 | """ |
|---|
| 489 | Retrieve changes data in a timerange. |
|---|
| 490 | """ |
|---|
| 491 | if fk_inventory: |
|---|
| 492 | data = self.cursor.execute("SELECT fk_category, short_descritpion,\ |
|---|
| 493 | entry_date, fk_inventory, fk_address, old_hostid, new_hostid \ |
|---|
| 494 | FROM _inventory_changes WHERE fk_inventory=? AND fk_address=? \ |
|---|
| 495 | AND entry_date >= ? AND entry_date < ? ORDER BY entry_date DESC", |
|---|
| 496 | (fk_inventory, fk_address, start, end)).fetchall() |
|---|
| 497 | else: |
|---|
| 498 | data = self.cursor.execute("SELECT fk_category, short_description,\ |
|---|
| 499 | entry_date, fk_inventory, fk_address, old_hostid, new_hostid \ |
|---|
| 500 | FROM _inventory_changes WHERE entry_date >= ? AND \ |
|---|
| 501 | entry_date < ? ORDER BY entry_date DESC", (start, end)).fetchall() |
|---|
| 502 | |
|---|
| 503 | return data |
|---|
| 504 | |
|---|
| 505 | def timerange_changes_categoryid_data(self, fk_category, start, end, |
|---|
| 506 | fk_inventory=None, fk_address=None): |
|---|
| 507 | """ |
|---|
| 508 | Retrieve changes data in a timerange for an especific category id. |
|---|
| 509 | """ |
|---|
| 510 | if fk_inventory: |
|---|
| 511 | data = self.cursor.execute("SELECT fk_category, short_description,\ |
|---|
| 512 | entry_date, fk_inventory, fk_address, old_hostid, new_hostid \ |
|---|
| 513 | FROM _inventory_changes WHERE fk_inventory=? AND fk_address=? AND \ |
|---|
| 514 | fk_category=? AND entry_date >= ? AND entry_date < ? \ |
|---|
| 515 | ORDER BY entry_date DESC", (fk_inventory, fk_address, fk_category, |
|---|
| 516 | start, end)).fetchall() |
|---|
| 517 | else: |
|---|
| 518 | data = self.cursor.execute("SELECT fk_category, short_description,\ |
|---|
| 519 | entry_date, fk_inventory, fk_address, old_hostid, new_hostid \ |
|---|
| 520 | FROM _inventory_changes WHERE fk_category=? AND entry_date >= ? \ |
|---|
| 521 | AND entry_date < ? ORDER BY entry_date DESC", (fk_category, start, |
|---|
| 522 | end)).fetchall() |
|---|
| 523 | |
|---|
| 524 | return data |
|---|
| 525 | |
|---|
| 526 | |
|---|
| 527 | def timerange_changes_categoryname_data(self, category, start, end): |
|---|
| 528 | """ |
|---|
| 529 | Retrieve changes data in a timerange for an especific category name. |
|---|
| 530 | """ |
|---|
| 531 | data = self.cursor.execute("SELECT fk_category, short_description, \ |
|---|
| 532 | entry_date, fk_inventory, fk_address, old_hostid, new_hostid \ |
|---|
| 533 | FROM _inventory_changes JOIN inventory_change_category as icc ON \ |
|---|
| 534 | (_inventory_changes.fk_category = icc.pk) WHERE icc.name=? AND \ |
|---|
| 535 | entry_date >= ? AND entry_date < ? \ |
|---|
| 536 | ORDER BY entry_date DESC", (category, start, end)).fetchall() |
|---|
| 537 | |
|---|
| 538 | return data |
|---|
| 539 | |
|---|
| 540 | |
|---|
| 541 | def timerange_changes_count_generic(self, start, end, category, inventory, |
|---|
| 542 | hostaddr): |
|---|
| 543 | """ |
|---|
| 544 | Selects what method to use to grab changes count. |
|---|
| 545 | """ |
|---|
| 546 | # start and end should always be present |
|---|
| 547 | if not start or not end: |
|---|
| 548 | raise BadParams("You should especify range start and range end") |
|---|
| 549 | |
|---|
| 550 | # check for missing args for inventory and hostaddr |
|---|
| 551 | if inventory and not hostaddr or not inventory and hostaddr: |
|---|
| 552 | raise BadParams("You should especify a hostaddr and inventory") |
|---|
| 553 | |
|---|
| 554 | |
|---|
| 555 | if category: |
|---|
| 556 | return self.timerange_changes_categoryid_count(category, start, |
|---|
| 557 | end, inventory, |
|---|
| 558 | hostaddr) |
|---|
| 559 | else: |
|---|
| 560 | return self.timerange_changes_count(start, end, inventory, |
|---|
| 561 | hostaddr) |
|---|
| 562 | |
|---|
| 563 | |
|---|
| 564 | def timerange_changes_count(self, start, end, fk_inventory=None, |
|---|
| 565 | fk_hostaddr=None): |
|---|
| 566 | """ |
|---|
| 567 | Get number of changes in a timerange. |
|---|
| 568 | """ |
|---|
| 569 | if fk_inventory: |
|---|
| 570 | count = self.cursor.execute("SELECT pk FROM _inventory_changes \ |
|---|
| 571 | WHERE fk_inventory=? AND fk_address=? AND \ |
|---|
| 572 | entry_date >= ? AND entry_date < ?", |
|---|
| 573 | (fk_inventory, fk_hostaddr, start, |
|---|
| 574 | end)).fetchall() |
|---|
| 575 | else: |
|---|
| 576 | count = self.cursor.execute("SELECT pk FROM _inventory_changes \ |
|---|
| 577 | WHERE entry_date >= ? AND entry_date < ?", |
|---|
| 578 | (start, end)).fetchall() |
|---|
| 579 | |
|---|
| 580 | return len(count) |
|---|
| 581 | |
|---|
| 582 | |
|---|
| 583 | def timerange_changes_categoryid_count(self, fk_category, start, end, |
|---|
| 584 | fk_inventory=None, |
|---|
| 585 | fk_hostaddr=None): |
|---|
| 586 | """ |
|---|
| 587 | Get number of changes in a timerange for an especific category id. |
|---|
| 588 | """ |
|---|
| 589 | if fk_inventory: |
|---|
| 590 | self.cursor.execute("SELECT pk FROM _inventory_changes WHERE \ |
|---|
| 591 | fk_category=? AND fk_inventory=? AND \ |
|---|
| 592 | fk_address=? AND entry_date >= ? AND \ |
|---|
| 593 | entry_date < ?", (fk_category, |
|---|
| 594 | fk_inventory, fk_hostaddr, start, end)) |
|---|
| 595 | else: |
|---|
| 596 | self.cursor.execute("SELECT pk FROM _inventory_changes WHERE \ |
|---|
| 597 | fk_category=? AND entry_date >= ? AND \ |
|---|
| 598 | entry_date < ?", (fk_category, start, end)) |
|---|
| 599 | |
|---|
| 600 | count = self.cursor.fetchall() |
|---|
| 601 | |
|---|
| 602 | return len(count) |
|---|
| 603 | |
|---|
| 604 | |
|---|
| 605 | def timerange_changes_categoryname_count(self, category, start, end): |
|---|
| 606 | """ |
|---|
| 607 | Get number of changes in a timerange for an especific category name. |
|---|
| 608 | """ |
|---|
| 609 | # not being used |
|---|
| 610 | count = self.cursor.execute("SELECT pk FROM _inventory_changes JOIN \ |
|---|
| 611 | inventory_change_category as icc ON \ |
|---|
| 612 | (_inventory_changes.fk_category = icc.pk) WHERE \ |
|---|
| 613 | icc.name=? AND entry_date > ? AND entry_date < ?", |
|---|
| 614 | (category, start, end)).fetchall() |
|---|
| 615 | |
|---|
| 616 | return len(count) |
|---|
| 617 | |
|---|
| 618 | |
|---|