PageRenderTime 50ms CodeModel.GetById 37ms RepoModel.GetById 0ms app.codeStats 1ms

/subsystem/seeddb/src/seeddb.py

https://bitbucket.org/lkarsten/nav-lkarsten
Python | 6511 lines | 6046 code | 169 blank | 296 comment | 298 complexity | 5b7aca96e4146190544e602efba5aa37 MD5 | raw file
Possible License(s): AGPL-1.0, GPL-2.0, BSD-3-Clause, Apache-2.0
  1. # -*- coding: ISO8859-1 -*-
  2. # $Id$
  3. #
  4. # Copyright 2003, 2004 Norwegian University of Science and Technology
  5. #
  6. # This file is part of Network Administration Visualized (NAV)
  7. #
  8. # NAV is free software; you can redistribute it and/or modify
  9. # it under the terms of the GNU General Public License as published by
  10. # the Free Software Foundation; either version 2 of the License, or
  11. # (at your option) any later version.
  12. #
  13. # NAV is distributed in the hope that it will be useful,
  14. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  15. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  16. # GNU General Public License for more details.
  17. #
  18. # You should have received a copy of the GNU General Public License
  19. # along with NAV; if not, write to the Free Software
  20. # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
  21. #
  22. #
  23. # Authors: Hans J?rgen Hoel <hansjorg@orakel.ntnu.no>
  24. #
  25. #################################################
  26. ## Imports
  27. import seeddbTables
  28. import nav.Snmp
  29. import sys
  30. import re
  31. import copy
  32. import initBox
  33. import forgetSQL
  34. import nav.web
  35. from nav.db import manage
  36. import nav.util
  37. from mod_python import util,apache
  38. from seeddbSQL import *
  39. from socket import gethostbyaddr,gethostbyname,gaierror
  40. from nav.web.serviceHelper import getCheckers,getDescription
  41. from nav.web.selectTree import selectTree,selectTreeLayoutBox
  42. from nav.web.selectTree import simpleSelect,updateSelect
  43. # Temporary fix:
  44. mod = __import__('encodings.utf_8',globals(),locals(),'*')
  45. mod = __import__('encodings.utf_16_be',globals(),locals(),'*')
  46. mod = __import__('encodings.latin_1',globals(),locals(),'*')
  47. mod = __import__('encodings.utf_16',globals(),locals(),'*')
  48. #################################################
  49. ## Templates
  50. from nav.web.templates.seeddbTemplate import seeddbTemplate
  51. #################################################
  52. ## Constants
  53. BASEPATH = '/seeddb/'
  54. CONFIGFILE = 'seeddb.conf'
  55. EDITPATH = [('Home','/'), ('Seed Database',BASEPATH)]
  56. ADDNEW_ENTRY = 'addnew_entry'
  57. UPDATE_ENTRY = 'update_entry'
  58. IGNORE_BOX = 'ignore_this_box'
  59. # Bulk import images
  60. BULK_IMG_GREEN = '/images/lys/green.png'
  61. BULK_IMG_YELLOW = '/images/lys/yellow.png'
  62. BULK_IMG_RED = '/images/lys/red.png'
  63. # Bulk import status
  64. BULK_STATUS_OK = 1
  65. BULK_STATUS_YELLOW_ERROR = 2
  66. BULK_STATUS_RED_ERROR = 3
  67. # Bulk fieldname for unspecified fields
  68. BULK_UNSPECIFIED_FIELDNAME = 'excess'
  69. # REQ_TRUE: a required field
  70. # REQ_FALSE: not required
  71. # REQ_NONEMPTY: not required, but don't insert empty field into db
  72. REQ_TRUE = 1
  73. REQ_FALSE = 2
  74. REQ_NONEMPTY = 3
  75. # Fieldtypes
  76. FIELD_STRING = 1
  77. FIELD_INTEGER = 2
  78. #################################################
  79. ## Functions
  80. def handler(req):
  81. ''' mod_python handler '''
  82. path = req.uri
  83. match = re.search('seeddb/(.+)$',path)
  84. if match:
  85. request = match.group(1)
  86. request = request.split('/')
  87. else:
  88. request = ""
  89. # Read configuration file
  90. #if not globals().has_key('CONFIG_CACHED'):
  91. readConfig()
  92. # Get form from request object
  93. keep_blank_values = True
  94. fieldStorage = util.FieldStorage(req,keep_blank_values)
  95. form = {}
  96. for field in fieldStorage.list:
  97. if form.has_key(field.name):
  98. # This input name already exists
  99. if type(form[field.name]) is list:
  100. # and it's already a list, so just append the
  101. # the new value
  102. form[field.name].append(str(field.value))
  103. else:
  104. # it's a string, so make a list and append the
  105. # new value to it
  106. valueList = []
  107. valueList.append(form[field.name])
  108. valueList.append(str(field.value))
  109. form[field.name] = valueList
  110. else:
  111. form[field.name] = str(field.value)
  112. # Check that all input is in the default encoding (utf8)
  113. unicodeError = False
  114. try:
  115. for key,value in form.items():
  116. if type(value) is list:
  117. for field in value:
  118. unicode_str = field.decode(DEFAULT_ENCODING)
  119. else:
  120. unicode_str = value.decode(DEFAULT_ENCODING)
  121. except UnicodeError:
  122. # Some of the input values is not in the default encoding
  123. unicodeError = True
  124. # Set form in request object
  125. req.form = form
  126. output = None
  127. showHelp = False
  128. if len(request) == 2:
  129. if request[0] == 'help':
  130. showHelp = True
  131. request = []
  132. if not len(request) > 1:
  133. output = index(req,showHelp)
  134. else:
  135. table = request[0]
  136. action = request[1]
  137. if table == 'bulk':
  138. output = bulkImport(req,action)
  139. elif pageList.has_key(table):
  140. output = editPage(req,pageList[table](),request,unicodeError)
  141. if output:
  142. req.content_type = "text/html"
  143. req.write(output)
  144. return apache.OK
  145. else:
  146. return apache.HTTP_NOT_FOUND
  147. def readConfig():
  148. ''' Reads configuration from seeddb.conf and sets global
  149. variables. '''
  150. global CONFIG_CACHED,DEFAULT_ENCODING,BULK_TRY_ENCODINGS,\
  151. CATEGORY_LIST,SPLIT_LIST,SPLIT_OPPOSITE
  152. config = nav.config.readConfig(CONFIGFILE)
  153. CONFIG_CACHED = True
  154. DEFAULT_ENCODING = config['default_encoding']
  155. BULK_TRY_ENCODINGS = eval(config['bulk_try_encodings'])
  156. # Make list of cable categories
  157. catlist = eval(config['categories'])
  158. categories = []
  159. for cat in catlist:
  160. categories.append(eval(config[cat]))
  161. CATEGORY_LIST = categories
  162. # Make list of splits and dict of opposite splits
  163. splitlist = eval(config['splits'])
  164. splits = []
  165. opposite = {}
  166. for splitName in splitlist:
  167. split = eval(config[splitName])
  168. # id=0, descr=1, opposite=2
  169. splits.append((split[0],split[1]))
  170. if split[2]:
  171. # References another split, set id
  172. opposite[split[0]] = eval(config[split[2]])[0]
  173. else:
  174. opposite[split[0]] = None
  175. SPLIT_LIST = splits
  176. SPLIT_OPPOSITE = opposite
  177. def index(req,showHelp=False,status=None):
  178. ''' Generates the index page (main menu) '''
  179. # Empty body
  180. class body:
  181. ''' Empty struct for template '''
  182. def __init__(self):
  183. pass
  184. body.status = status
  185. body.title = 'Seed Database - Modify seed information for the NAV database'
  186. body.infotext = 'Here you can add, delete or edit seed information ' +\
  187. 'that are needed for the NAV database. Keep in mind ' +\
  188. 'that most of the data in the NAV database are ' +\
  189. 'collected automatically by NAV background processes.'
  190. body.showHelp = showHelp
  191. body.help = [BASEPATH + 'help/','Show help']
  192. body.nohelp = [BASEPATH,'Hide help']
  193. body.tables = []
  194. headings = []
  195. # Table for boxes and services
  196. rows = [['IP devices',
  197. 'Input seed information on the IP devices you want to ' +\
  198. 'monitor',
  199. [BASEPATH + 'netbox/edit','Add'],
  200. [BASEPATH + 'netbox/list','Edit'],
  201. [BASEPATH + 'bulk/netbox','Bulk import']],
  202. ['Services',
  203. 'Which services on which servers do you want to monitor?',
  204. [BASEPATH + 'service/edit','Add'],
  205. [BASEPATH + 'service/list','Edit'],
  206. [BASEPATH + 'bulk/service','Bulk import']]]
  207. body.tables.append(Table('IP devices and services','',headings,rows))
  208. # Table for rooms and locations
  209. rows = [['Room',
  210. 'Register all wiring closets and server rooms that contain ' +\
  211. 'IP devices which NAV monitors',
  212. [BASEPATH + 'room/edit','Add'],
  213. [BASEPATH + 'room/list','Edit'],
  214. [BASEPATH + 'bulk/room','Bulk import']],
  215. ['Location',
  216. 'Rooms are organised in locations',
  217. [BASEPATH + 'location/edit','Add'],
  218. [BASEPATH + 'location/list','Edit'],
  219. [BASEPATH + 'bulk/location','Bulk import']]]
  220. body.tables.append(Table('Rooms and locations','',headings,rows))
  221. # Table org and usage cat
  222. rows = [['Organisation',
  223. 'Register all organisational units that are relevant. I.e. ' +\
  224. 'all units that have their own subnet/server facilities.',
  225. [BASEPATH + 'org/edit','Add'],
  226. [BASEPATH + 'org/list','Edit'],
  227. [BASEPATH + 'bulk/org','Bulk import']],
  228. ['Usage categories',
  229. 'NAV encourages a structure in the subnet structure. ' +\
  230. 'Typically a subnet has users from an organisational ' +\
  231. 'unit. In addition this may be subdivided into a ' +\
  232. 'category of users, i.e. students, employees, ' +\
  233. 'administration etc.',
  234. [BASEPATH + 'usage/edit','Add'],
  235. [BASEPATH + 'usage/list','Edit'],
  236. [BASEPATH + 'bulk/usage','Bulk import']]]
  237. body.tables.append(Table('Organisation and usage categories','',
  238. headings,rows))
  239. # Table for types, products and vendors
  240. rows = [['Type',
  241. 'The type describes the type of network device, uniquely ' +\
  242. 'described from the SNMP sysobjectID',
  243. [BASEPATH + 'type/edit','Add'],
  244. [BASEPATH + 'type/list','Edit'],
  245. [BASEPATH + 'bulk/type','Bulk import']],
  246. ['Product',
  247. 'Similar to type, but with focus on the product number and ' +\
  248. 'description. A product may be a type, it may also be a ' +\
  249. 'component (i.e. module) within an equipment type',
  250. [BASEPATH + 'product/edit','Add'],
  251. [BASEPATH + 'product/list','Edit'],
  252. [BASEPATH + 'bulk/product','Bulk import']],
  253. ['Vendor',
  254. 'Register the vendors that manufacture equipment that are ' +\
  255. 'represented in your network.',
  256. [BASEPATH + 'vendor/edit','Add'],
  257. [BASEPATH + 'vendor/list','Edit'],
  258. [BASEPATH + 'bulk/vendor','Bulk import']],
  259. ['Snmpoid',
  260. 'Manually add snmpoids (candidates for the cricket collector)',
  261. [BASEPATH + 'snmpoid/edit','Add'],
  262. ['',''],
  263. ['','']],
  264. ['Subcategory',
  265. 'The main categories of a device are predefined by NAV (i.e. ' +\
  266. 'GW,SW,SRV). You may however create subcategories yourself.',
  267. [BASEPATH + 'subcat/edit','Add'],
  268. [BASEPATH + 'subcat/list','Edit'],
  269. [BASEPATH + 'bulk/subcat','Bulk import']],
  270. ]
  271. body.tables.append(Table('Types, products and vendors','',headings,rows))
  272. # Table for vlans and special subnets
  273. rows = [['Vlan',
  274. 'Register the vlan number that are in use (this info may ' +\
  275. 'also be derived automatically from the routers)',
  276. None,
  277. [BASEPATH + 'vlan/list','Edit'],
  278. None],
  279. ['Prefix',
  280. 'Register special ip prefixes. Typically reserved prefixes ' +\
  281. 'or prefixes that are not directly connected to monitored ' +\
  282. 'routers/firewalls fall into this category',
  283. [BASEPATH + 'prefix/edit','Add'],
  284. [BASEPATH + 'prefix/list','Edit'],
  285. [BASEPATH + 'bulk/prefix','Bulk import']]]
  286. body.tables.append(Table('Vlans and special subnets','',headings,rows))
  287. # Table for cabling and patch
  288. rows = [['Cabling',
  289. 'Here you may document the horizontal cabling system ',
  290. [BASEPATH + 'cabling/edit','Add'],
  291. [BASEPATH + 'cabling/list','Edit'],
  292. [BASEPATH + 'bulk/cabling','Bulk import']],
  293. ['Patch',
  294. 'Register the cross connects in the wiring closets ',
  295. [BASEPATH + 'patch/edit','Add'],
  296. [BASEPATH + 'patch/list','Edit'],
  297. [BASEPATH + 'bulk/patch','Bulk import']]]
  298. body.tables.append(Table('Cabling system','',headings,rows))
  299. nameSpace = {'entryList': None, 'editList': None, 'editForm': None, 'body': body}
  300. template = seeddbTemplate(searchList=[nameSpace])
  301. template.path = [('Home','/'),
  302. ('Seed Database',None)]
  303. return template.respond()
  304. ######################
  305. ##
  306. ## General functions
  307. ##
  308. ########################
  309. # General function for handling editing
  310. def editPage(req,page,request,unicodeError):
  311. ''' General handler function for all editpages. Whenever an action
  312. add, update, list or delete is performed, this function is called. '''
  313. # Cancel button redirect
  314. if req.form.has_key(editForm.cnameCancel):
  315. nav.web.redirect(req,BASEPATH,seeOther=True)
  316. # Make a status object
  317. status = seeddbStatus()
  318. # Get action from request (url)
  319. action = request[1]
  320. selected = []
  321. addedId = None
  322. # Get editid from url if it is present (external links and list links)
  323. if len(request) > 1:
  324. if (len(request) == 3) or (len(request) == 4):
  325. if request[2]:
  326. selected = [request[2]]
  327. # Make a list of selected entries from a posted selectlist
  328. if req.form.has_key(selectList.cnameChk):
  329. if type(req.form[selectList.cnameChk]) is str:
  330. # only one selected
  331. selected = [req.form[selectList.cnameChk]]
  332. elif type(req.form[selectList.cnameChk]) is list:
  333. # more than one selected
  334. for s in req.form[selectList.cnameChk]:
  335. selected.append(s)
  336. # Remember entries which we are already editing
  337. # Used if editing is interrupted by an error
  338. if req.form.has_key(UPDATE_ENTRY):
  339. if type(req.form[UPDATE_ENTRY]) is str:
  340. # only one selected
  341. selected = [req.form[UPDATE_ENTRY]]
  342. elif type(req.form[UPDATE_ENTRY]) is list:
  343. # more than one selected
  344. for s in req.form[UPDATE_ENTRY]:
  345. selected.append(s)
  346. # Disallow adding (for pageVlan)
  347. if req.form.has_key(selectList.cnameAdd) and hasattr(page,'disallowAdd'):
  348. status.errors.append(page.disallowAddReason)
  349. action = 'list'
  350. # Check if any entries are selected when action is 'edit' or 'delete'
  351. if action == 'edit':
  352. if req.form.has_key(selectList.cnameEdit):
  353. if not selected:
  354. status.errors.append('No entries selected for editing')
  355. action = 'list'
  356. elif req.form.has_key(selectList.cnameDelete):
  357. action = 'delete'
  358. if not selected:
  359. status.errors.append('No entries selected')
  360. action = 'list'
  361. else:
  362. if not selected:
  363. action = 'add'
  364. # Check for unicode errors in input
  365. if unicodeError:
  366. status.errors.append('The data you input was sent in a ' +\
  367. 'non-recognisible encoding. Make sure your '
  368. 'browser uses automatic character encoding ' +\
  369. 'or set it to \'' + str(DEFAULT_ENCODING) + '\'.')
  370. action = 'list'
  371. # Set 'current path'
  372. path = page.pathAdd
  373. templatebox = page.editbox(page)
  374. # Copy field defintions from the main templatebox (used by add/update)
  375. page.fields = templatebox.fields
  376. # Make form object for template
  377. outputForm = editForm()
  378. if hasattr(page,'action'):
  379. outputForm.action = page.action
  380. else:
  381. outputForm.action = page.basePath + 'edit'
  382. # List definition, get sorting parameter
  383. sort = None
  384. if req.form.has_key('sort'):
  385. sort = req.form['sort']
  386. listView = page.listDef(req,page,sort)
  387. # Check if the confirm button has been pressed
  388. if req.form.has_key(outputForm.cnameConfirm):
  389. missing = templatebox.hasMissing(req)
  390. if not missing:
  391. status = templatebox.verifyFields(req,status)
  392. if not len(status.errors):
  393. if req.form.has_key(ADDNEW_ENTRY):
  394. # add new entry
  395. (status,action,outputForm,addedId) = page.add(req,
  396. outputForm,action)
  397. elif req.form.has_key(UPDATE_ENTRY):
  398. # update entry
  399. (status,action,outputForm,selected) = page.update(req,
  400. outputForm,
  401. selected)
  402. else:
  403. status.errors.append("Required field '" + missing + "' missing")
  404. # Confirm delete pressed?
  405. elif req.form.has_key(selectList.cnameDeleteConfirm):
  406. status = page.delete(selected,status)
  407. outputForm = None
  408. selected = None
  409. action = 'list'
  410. # Decide what to show
  411. if action == 'predefined':
  412. # Action is predefined by addNetbox() or updateNetbox()
  413. outputForm.textConfirm = 'Continue'
  414. outputForm.action = action
  415. outputForm.status = status
  416. listView = None
  417. elif action == 'edit':
  418. path = page.pathEdit
  419. title = 'Edit '
  420. if len(selected) > 1:
  421. title += page.plural
  422. else:
  423. title += page.singular
  424. outputForm.title = title
  425. outputForm.action = action
  426. outputForm.status = status
  427. outputForm.textConfirm = 'Update'
  428. if page.editMultipleAllowed:
  429. # This page can edit multiple entries at a time
  430. for s in selected:
  431. outputForm.add(page.editbox(page,req,s,formData=req.form))
  432. else:
  433. # This page can only edit one entry at a time (eg. netbox)
  434. outputForm.add(page.editbox(page,req,selected[0],formData=req.form))
  435. # preserve path
  436. #outputForm.action = page.basePath + 'edit/' + selected[0]
  437. listView = None
  438. elif action == 'add':
  439. path = page.pathAdd
  440. outputForm.action = action
  441. outputForm.status = status
  442. outputForm.title = 'Add ' + page.singular
  443. outputForm.textConfirm = 'Add ' + page.singular
  444. outputForm.add(page.editbox(page,req,formData=req.form))
  445. listView = None
  446. elif action == 'delete':
  447. path = page.pathDelete
  448. listView = page.listDef(req,page,sort,selected)
  449. listView.status = status
  450. listView.fill(req)
  451. outputForm = None
  452. elif action == 'list':
  453. if addedId:
  454. listView.selectedId = addedId
  455. if selected:
  456. listView.selectedId = selected[0]
  457. path = page.pathList
  458. listView.status=status
  459. listView.fill(req)
  460. outputForm = None
  461. elif action == 'redirect':
  462. # Redirect to main page (snmpoid add)
  463. return index(req,status=status)
  464. nameSpace = {'entryList': listView,'editList': None,'editForm': outputForm}
  465. template = seeddbTemplate(searchList=[nameSpace])
  466. template.path = path
  467. return template.respond()
  468. def insertNetbox(ip,sysname,catid,roomid,orgid,
  469. ro,rw,deviceid,serial,
  470. typeid,snmpversion,subcatlist=None,
  471. function=None):
  472. ''' Inserts a netbox into the database. Used by pageNetbox.add(). '''
  473. if not deviceid:
  474. # Make new device first
  475. if len(serial):
  476. fields = {'serial': serial}
  477. else:
  478. # Don't insert an empty serialnumber (as serialnumbers must be
  479. # unique in the database) (ie. don't insert '' for serial)
  480. fields = {}
  481. deviceid = addEntryFields(fields,
  482. 'device',
  483. ('deviceid','device_deviceid_seq'))
  484. fields = {'ip': ip,
  485. 'roomid': roomid,
  486. 'deviceid': deviceid,
  487. 'sysname': sysname,
  488. 'catid': catid,
  489. 'orgid': orgid,
  490. 'ro': ro,
  491. 'rw': rw}
  492. #uptodate = false per default
  493. # Get prefixid
  494. query = "SELECT prefixid FROM prefix WHERE '%s'::inet << netaddr" \
  495. % (fields['ip'],)
  496. try:
  497. result = executeSQLreturn(query)
  498. fields['prefixid'] = str(result[0][0])
  499. except:
  500. pass
  501. if typeid:
  502. fields['typeid'] = typeid
  503. # Set uptyodate = false
  504. # This part is done in netbox now. And for a new box this
  505. # field defaults to 'f'
  506. #tifields = {'uptodate': 'f'}
  507. #updateEntryFields(tifields,'type','typeid',typeid)
  508. if snmpversion:
  509. # Only use the first char from initbox, can't insert eg. '2c' in
  510. # this field
  511. snmpversion = snmpversion[0]
  512. fields['snmp_version'] = snmpversion
  513. netboxid = addEntryFields(fields,
  514. 'netbox',
  515. ('netboxid','netbox_netboxid_seq'))
  516. # If subcatlist and function is given, insert them
  517. if subcatlist:
  518. if type(subcatlist) is list:
  519. for sc in subcatlist:
  520. fields = {'netboxid': netboxid,
  521. 'category': sc}
  522. addEntryFields(fields,'netboxcategory')
  523. else:
  524. fields = {'netboxid': netboxid,
  525. 'category': subcatlist}
  526. addEntryFields(fields,'netboxcategory')
  527. if function:
  528. fields = {'netboxid': netboxid,
  529. 'key': '',
  530. 'var': 'function',
  531. 'val': function}
  532. addEntryFields(fields,'netboxinfo')
  533. ######################
  534. ##
  535. ## General classes
  536. ##
  537. ########################
  538. class Table:
  539. ''' A general class for html tables used by index(). '''
  540. def __init__(self,title,infotext,headings,rows):
  541. self.title = title
  542. self.infotext = infotext
  543. self.headings = headings
  544. self.rows = rows
  545. class seeddbStatus:
  546. ''' Struct class which holds two lists (messages and errors). Every
  547. form object got an instance of this class and uses it to add
  548. messages and errors which is then displayed by the template. '''
  549. # List of status messages, one line per message
  550. messages = []
  551. # List of error messages, one line per message
  552. errors = []
  553. def __init__(self):
  554. self.messages = []
  555. self.errors = []
  556. class entryListCell:
  557. ''' Represents a cell (TD) in a selectlist object. '''
  558. CHECKBOX = 'chk'
  559. RADIO = 'rad'
  560. HIDDEN = 'hid'
  561. def __init__(self,text=None,url=None,buttonType=None,
  562. image=None,tooltip=None):
  563. self.text = text
  564. self.url = url
  565. self.buttonType = buttonType
  566. self.image = image
  567. self.tooltip = tooltip
  568. ## class entryList (rename to selectList)
  569. class entryList:
  570. ''' Flexible class for making lists of entries which can be selected.
  571. Used by all 'edit' pages.
  572. Uses the list definitions defined in every page class.
  573. descriptionFormat = [(text,forgetSQLfield),(text,...] '''
  574. # Constants
  575. CNAME_SELECT = 'checkbox_id'
  576. CNAME_ADD = 'submit_add'
  577. CNAME_EDIT = 'submit_edit'
  578. CNAME_DELETE = 'submit_delete'
  579. CNAME_CONFIRM_DELETE = 'confirm_delete'
  580. CNAME_CANCEL = 'form_cancel'
  581. # Class variables used by the template
  582. title = None
  583. status = None
  584. body = None
  585. formMethod = 'post'
  586. formAction = None
  587. selectCname = CNAME_SELECT
  588. buttonsTop = [(CNAME_ADD,'Add new'),
  589. (CNAME_EDIT,'Edit selected'),
  590. (CNAME_DELETE,'Delete selected')]
  591. buttonsBottom = buttonsTop
  592. hideFirstHeading = False # Don't show first column heading (usually
  593. # the select heading) if True
  594. buttonTypeOverride = None # override the chosen select button type
  595. # used for bulk and delete lists where there
  596. # is no checkbox/radiobut., only hidden
  597. headings = [] # list of cell objects
  598. rows = [] # tuples of (sortstring,id,cell object)
  599. # Variables for filling the list
  600. tableName = None
  601. basePath = None
  602. sortBy = None # Sort by columnumber
  603. defaultSortBy = None # Default columnnumber sorted by
  604. headingDefinition = None # list of tuples (heading,show sort link)
  605. cellDefintion = None # cellDefinition list
  606. where = None # List of id's (strings)
  607. sortingOn = True # Show links for sorting the list
  608. # SQL filters
  609. filters = None
  610. filterConfirm = 'cn_filter'
  611. filterConfirmText = 'Update list'
  612. selectedId = None
  613. def __init__(self,req,struct,sort,deleteWhere=None):
  614. self.headings = []
  615. self.rows = []
  616. if sort:
  617. sort = int(sort)
  618. self.sortBy = sort
  619. self.tableName = struct.tableName
  620. self.tableIdKey = struct.tableIdKey
  621. self.basePath = struct.basePath
  622. self.deleteWhere = deleteWhere
  623. self.formAction = self.basePath + 'edit'
  624. self.filterAction = self.basePath + 'list'
  625. if deleteWhere:
  626. self.buttonTypeOverride = entryListCell.HIDDEN
  627. self.hideFirstHeading = True
  628. self.where = deleteWhere
  629. title = 'Are you sure you want to delete the ' + \
  630. 'selected '
  631. if len(deleteWhere) > 1:
  632. title += struct.plural
  633. else:
  634. title += struct.singular
  635. self.title = title + '?'
  636. self.sortingOn = False
  637. self.buttonsTop = None
  638. self.buttonsBottom = [(self.CNAME_CONFIRM_DELETE, 'Delete'),
  639. (self.CNAME_CANCEL, 'Cancel')]
  640. else:
  641. self.title = 'Edit ' + struct.plural
  642. self.sortingOn = True
  643. def fill(self,req):
  644. """ Fill the list with data from the database. """
  645. # No filters if this is a delete list
  646. if self.deleteWhere:
  647. self.filters = None
  648. # Make filters
  649. if self.filters:
  650. self.makeFilters(req)
  651. # Make headings
  652. i = 0
  653. for heading,sortlink,sortFunction in self.headingDefinition:
  654. if self.hideFirstHeading:
  655. heading = ''
  656. self.hideFirstHeading = False
  657. if self.sortBy:
  658. currentOrder = self.sortBy
  659. else:
  660. currentOrder = self.defaultSortBy
  661. s = i
  662. if i == currentOrder:
  663. # Reverse sort?
  664. s = -i
  665. url = self.basePath + 'list?sort=' + str(s)
  666. if sortlink and self.sortingOn:
  667. self.headings.append(entryListCell(heading,
  668. url))
  669. else:
  670. self.headings.append(entryListCell(heading,
  671. None))
  672. i = i + 1
  673. # Check filters and decide if we're going to show list
  674. renderList = True
  675. filterSettings = []
  676. if self.filters:
  677. renderList = False
  678. for filter in self.filters:
  679. # filter[10] is selected id in filter
  680. if filter[10]:
  681. # Something is selected, show list
  682. renderList = True
  683. # filter[6] is tableIdKey
  684. filterSettings.append((filter[10],filter[6]))
  685. # Skip filling if no result from filter
  686. if renderList:
  687. # Preparse tooltips, etc.
  688. for sqlQuery,definition in self.cellDefinition:
  689. for column in definition:
  690. for cell in column:
  691. if type(cell) is list:
  692. # This cell definition is a list, as opposed to
  693. # a tuple, so we must prefetch some data for the
  694. # parse function
  695. # Must prefetch data for this column
  696. for tooltipDef in cell:
  697. # There can be one or more defintions per cell
  698. sql = tooltipDef[0]
  699. # column[2] is reserved for data
  700. tooltipDef[2] = executeSQLreturn(sql)
  701. # Make rows
  702. reverseSort = False
  703. if self.sortBy:
  704. if self.sortBy < 0:
  705. self.sortBy = self.sortBy * -1
  706. reverseSort = True
  707. for sqlTuple,definition in self.cellDefinition:
  708. # Create SQL query from tuple
  709. columns,tablenames,join,where,orderBy = sqlTuple
  710. sqlQuery = 'SELECT ' + columns + ' FROM ' + tablenames
  711. if join:
  712. sqlQuery += ' %s ' % (join,)
  713. if where:
  714. sqlQuery += ' WHERE ' + where
  715. # Add where clause if self.where is present
  716. if self.where:
  717. if not where:
  718. # No where defined in sqlTuple, so add it now
  719. sqlQuery += ' WHERE '
  720. else:
  721. # Else, these are additional so add AND
  722. sqlQuery += ' AND '
  723. first = True
  724. sqlQuery += ' ('
  725. for id in self.where:
  726. if not first:
  727. sqlQuery += 'OR'
  728. sqlQuery += " %s.%s='%s' " % (self.tableName,
  729. self.tableIdKey,id)
  730. if first:
  731. first = False
  732. sqlQuery += ') '
  733. # Add where clause if filterSettings is present
  734. for filter in filterSettings:
  735. if not where:
  736. # No where defined in sqlTuple, so add it now
  737. sqlQuery += ' WHERE '
  738. else:
  739. # Else, these are additional so add AND
  740. sqlQuery += ' AND '
  741. sqlQuery += filter[1] + "='" + filter[0] + "' "
  742. if orderBy:
  743. sqlQuery += ' ORDER BY ' + orderBy
  744. fetched = executeSQLreturn(sqlQuery)
  745. for row in fetched:
  746. id = row[0]
  747. cells = []
  748. for text,url,buttonType,image,tooltip in definition:
  749. if buttonType and self.buttonTypeOverride:
  750. buttonType = self.buttonTypeOverride
  751. cells.append(entryListCell(self.parse(text,row),
  752. self.parse(url,row,True),
  753. buttonType,
  754. image,
  755. self.parse(tooltip,row)))
  756. sortKey = None
  757. if self.sortBy:
  758. sortKey = row[self.sortBy]
  759. self.rows.append([sortKey,(id,cells)])
  760. if self.sortBy:
  761. if self.headingDefinition[self.sortBy][2]:
  762. # Optional compare method
  763. self.rows.sort(self.headingDefinition[self.sortBy][2])
  764. else:
  765. self.rows.sort()
  766. if reverseSort:
  767. self.rows.reverse()
  768. def parse(self,parseString,currentRow,url=False):
  769. """ Parses format strings used by the list definitions. """
  770. result = None
  771. if type(parseString) is int:
  772. # parseString refers to integer column
  773. result = [currentRow[parseString]]
  774. elif type(parseString) is str:
  775. parseString = parseString.replace('{p}',self.basePath)
  776. parseString = parseString.replace('{id}',str(currentRow[0]))
  777. parseString = parseString.replace('{descr}',str(currentRow[1]))
  778. if parseString.find('SELECT') == 0:
  779. # This string is a sql query (used by vlan)
  780. resultString = ''
  781. sqlresult = executeSQLreturn(parseString)
  782. for row in sqlresult:
  783. for col in row:
  784. resultString += col + '<br>'
  785. result = [resultString]
  786. else:
  787. if url:
  788. result = parseString
  789. else:
  790. result = [parseString]
  791. elif type(parseString) is list:
  792. result = []
  793. for tooltipDef in parseString:
  794. data = tooltipDef[2]
  795. if data:
  796. # There is preparsed data (ie. the sql returned something)
  797. result.append(tooltipDef[1][0])
  798. # [1][0] = Tooltip (can also be other preparsed data) header
  799. for row in data:
  800. if currentRow[0] == row[0]:
  801. # ID's match
  802. result.append(row[1])
  803. return result
  804. def makeFilters(self,req):
  805. for filter in self.filters:
  806. text,firstEntry,sqlTuple,idFormat,optionFormat,\
  807. controlName,tableId,table,fields = filter
  808. optionsList = []
  809. if firstEntry:
  810. firstEntry = ([firstEntry[0]],[firstEntry[1]])
  811. optionsList.append(firstEntry)
  812. # Make sql
  813. sql = "SELECT " + sqlTuple[0] + " FROM " + sqlTuple[1] + " "
  814. if sqlTuple[2]:
  815. sql += sqlTuple[2] + " "
  816. if sqlTuple[3]:
  817. sql += "WHERE " + sqlTuple[3] + " "
  818. if sqlTuple[4]:
  819. sql += "ORDER BY " + sqlTuple[4]
  820. result = executeSQLreturn(sql)
  821. for row in result:
  822. id = self.parse(idFormat,row)
  823. text = self.parse(optionFormat,row)
  824. optionsList.append((id,text))
  825. filter.append(optionsList)
  826. # Selected id
  827. selected = None
  828. if self.selectedId:
  829. entry = table(self.selectedId)
  830. fieldList = fields.split('.')
  831. for field in fieldList:
  832. entry = getattr(entry,field)
  833. selected = str(entry)
  834. if req.form.has_key(controlName):
  835. if len(req.form[controlName]):
  836. selected = req.form[controlName]
  837. filter.append(selected)
  838. # Class representing a form, used by the template
  839. class editForm:
  840. """ Class representing a form element, the main component of every
  841. edit page. Each form element can have any number of editbox
  842. objects added to it. """
  843. # For the template
  844. method = 'post'
  845. action = None
  846. title = None
  847. error = None
  848. status = None
  849. backlink = None
  850. enctype = 'application/x-www-form-urlencoded'
  851. # Text and controlname
  852. textConfirm = None
  853. cnameConfirm = 'form_confirm'
  854. showConfirm = True
  855. textCancel = 'Cancel'
  856. cnameCancel = 'form_cancel'
  857. showCancel = True
  858. actionCancel = BASEPATH
  859. # Only used for netboxes (see template)
  860. # 'submit_delete' is fetched by the same handler as when deleting multiple
  861. # entities from a list
  862. textDelete = 'Delete'
  863. cnameDelete = 'submit_delete'
  864. showDelete = True
  865. # Used by edit netbox in the intermediate
  866. CNAME_CONTINUE = 'cname_continue'
  867. # List of editboxes to display
  868. editboxes = []
  869. def __init__(self,cnameConfirm=None):
  870. if cnameConfirm:
  871. self.cnameConfirm = cnameConfirm
  872. self.editboxes = []
  873. def add(self,box):
  874. """ Add an editbox object to this form element. """
  875. self.editboxes.append(box)
  876. class inputText:
  877. """ Class representing a textinput html control. """
  878. type = 'text'
  879. name = None
  880. maxlength = None
  881. def __init__(self,value='',size=22,maxlength=None,disabled=False):
  882. self.value = value
  883. self.disabled = disabled
  884. self.size = str(size)
  885. if maxlength:
  886. self.maxlength = str(maxlength)
  887. class inputTreeSelect:
  888. """ Container class for treeselects. Used to get treeselect in the
  889. same format as all the other inputs used by the template. """
  890. type = 'treeselect'
  891. name = None
  892. treeselect = None
  893. disabled = False
  894. def __init__(self,treeselect):
  895. self.value = ''
  896. self.treeselect = treeselect
  897. class inputSelect:
  898. """ Class representing a select input html control. """
  899. type = 'select'
  900. name = None
  901. def __init__(self,options=None,table=None,attribs=None,disabled=False):
  902. self.value = ''
  903. self.options = options
  904. self.attribs = attribs
  905. self.disabled = disabled
  906. if table:
  907. self.options = table.getOptions()
  908. class inputMultipleSelect:
  909. """ Class representing a multiple select input html control. """
  910. type = 'multipleselect'
  911. name = None
  912. value = []
  913. def __init__(self,options=None,table=None,disabled=False):
  914. self.options = options
  915. self.disabled = disabled
  916. if table:
  917. self.options = table.getOptions()
  918. class inputFile:
  919. """ Class representing a file upload input control. """
  920. type = 'file'
  921. name = None
  922. value = ''
  923. disabled = False
  924. def __init__(self):
  925. pass
  926. class inputTextArea:
  927. """ Class representing a textarea input html control. """
  928. type = 'textarea'
  929. name = None
  930. def __init__(self,rows=20,cols=80):
  931. self.rows = rows
  932. self.cols = cols
  933. self.value = ''
  934. self.disabled = False
  935. class inputCheckbox:
  936. """ Class representing a checkbox input html control. """
  937. type = 'checkbox'
  938. name = None
  939. def __init__(self,disabled=False):
  940. self.value = '0'
  941. self.disabled = disabled
  942. class inputHidden:
  943. """ Class representing a hidden input html control. """
  944. type = 'hidden'
  945. name = None
  946. disabled = False
  947. def __init__(self,value):
  948. self.value = value
  949. class inputServiceProperties:
  950. """ Contains a list of inputServiceProperty inputs.
  951. (used by pageService) """
  952. type = 'serviceproperties'
  953. disabled = False
  954. def __init__(self,propertyList):
  955. self.propertyList = propertyList
  956. class inputServiceProperty:
  957. """ Class representing a serviceproperty input box.
  958. (used by pageService) """
  959. type = 'serviceproperty'
  960. disabled = False
  961. def __init__(self,title,id,args,optargs,display=True):
  962. self.title = title
  963. self.id = id
  964. self.display = display
  965. self.args = args
  966. self.optargs = optargs
  967. class editbox:
  968. """ Parent class for all the different editboxes which are all added
  969. to an editform object. There are normally one editbox per page, but
  970. for some pages there are more (there are created three editboxes for
  971. the netbox page for example, editbox(main),editboxserial and
  972. editboxfunction (which also includes the subcat)).
  973. The editbox contains field defitions used by the template to render
  974. the forms and functions to fill the form with data from either the
  975. database or from a previous http post. """
  976. boxName = ADDNEW_ENTRY
  977. boxId = 0
  978. # Current box number keeps track of the box number when there are
  979. # more than one editbox in a form. Used by formFill to get correct data
  980. #currentBoxNumber = 0
  981. def fill(self):
  982. """ Fill this form with data from the database (entry = editId). """
  983. entry = self.table(self.editId)
  984. # Set the name of this box to reflect that we are
  985. # updating an entry
  986. self.boxName = UPDATE_ENTRY
  987. self.boxId = self.editId
  988. ## TEMPORARY:
  989. ## check if this is one of the new pages by checking
  990. ## for three instead of two entries in the desc list
  991. if len(self.fields[self.fields.keys()[0]]) > 2:
  992. # Uses psycopg to fill field values
  993. page = pageList[self.page]
  994. select = ''
  995. first = True
  996. keyNumber = {}
  997. i = 0
  998. for key in self.fields.keys():
  999. keyNumber[key] = i
  1000. i+=1
  1001. if not first:
  1002. select += ', '
  1003. select += key
  1004. first = False
  1005. tables = page.tableName
  1006. where = page.tableIdKey + "='" + self.editId + "'"
  1007. # For the benefit of pagePrefix (which must select from vlan too)
  1008. if hasattr(self,'additionalSQL'):
  1009. tables += ', vlan'
  1010. where += self.additionalSQL
  1011. sql = "SELECT %s FROM %s WHERE %s" % (select, tables, where)
  1012. result = executeSQLreturn(sql)
  1013. result = result[0]
  1014. for key,desc in self.fields.items():
  1015. value = result[keyNumber[key]]
  1016. if value:
  1017. desc[0].value = str(value)
  1018. else:
  1019. # Old style filling with forgetsql
  1020. for fieldname,desc in self.fields.items():
  1021. value = getattr(entry,fieldname)
  1022. if value:
  1023. desc[0].value = str(value)
  1024. def setControlNames(self,controlList=None):
  1025. """ Set controlnames for the inputs to the same as the fieldnames. """
  1026. if not controlList:
  1027. controlList = self.fields
  1028. for fieldname,desc in controlList.items():
  1029. desc[0].name = fieldname
  1030. def verifyFields(self,req,status):
  1031. """ Verify that data entered into fields are of correct type.
  1032. Eg. integers in FIELD_INTEGER fields. """
  1033. for field,desc in self.fields.items():
  1034. if req.form.has_key(field):
  1035. if type(req.form[field]) is list:
  1036. # Editing several entries
  1037. for each in req.form[field]:
  1038. # Do tests here
  1039. if desc[3] == FIELD_INTEGER:
  1040. if len(each):
  1041. try:
  1042. int(each)
  1043. except ValueError:
  1044. error = "Invalid integer: '" +\
  1045. str(each) + "'"
  1046. status.errors.append(error)
  1047. else:
  1048. # Editing only one field
  1049. # Do tests here
  1050. if desc[3] == FIELD_INTEGER:
  1051. try:
  1052. if len(req.form[field]):
  1053. int(req.form[field])
  1054. except ValueError:
  1055. error = "Invalid integer: '" + \
  1056. str(req.form[field]) + "'"
  1057. status.errors.append(error)
  1058. return status
  1059. def hasMissing(self,req):
  1060. """ Check if any of the required fields are missing in the req.form
  1061. Returns the name the first missing field, or False
  1062. Note: keep_blank_values (mod_python) must be True or empty fields
  1063. won't be present in the form """
  1064. missing = False
  1065. for field,desc in self.fields.items():
  1066. # Keep blank values must be switched on, or else the next line
  1067. # will fail, could be more robust
  1068. if req.form.has_key(field):
  1069. if type(req.form[field]) is list:
  1070. # the field is a list, several entries have been edited
  1071. for each in req.form[field]:
  1072. if desc[1] == REQ_TRUE:
  1073. # this field is required
  1074. if not len(each):
  1075. if len(desc) > 2:
  1076. # desc[2] is real fieldname
  1077. missing = desc[2]
  1078. else:
  1079. # cryptic fieldname (remove this later)
  1080. missing = field
  1081. break
  1082. else:
  1083. if desc[1] == REQ_TRUE:
  1084. # tihs field is required
  1085. if not len(req.form[field]):
  1086. if len(desc) > 2:
  1087. # desc[2] is real fieldname
  1088. missing = desc[2]
  1089. else:
  1090. # cryptic fieldname (remove this later)
  1091. missing = field
  1092. break
  1093. return missing
  1094. def addHidden(self,fieldname,value):
  1095. """ Add hidden html input control to the editbox. """
  1096. self.hiddenFields[fieldname] = [inputHidden(value),False]
  1097. self.hiddenFields[fieldname][0].name = fieldname
  1098. def addDisabled(self):
  1099. """ Since fields which are disabled, aren't posted (stupid HTML)
  1100. we must add them as hidden fields.
  1101. This only goes for textinputs (?!) so we must also change
  1102. controlnames to avoid getting double values for selects, etc. """
  1103. for fieldname,definition in self.fields.items():
  1104. if definition[0].disabled and (not definition[0].type=='hidden'):
  1105. self.addHidden(fieldname,definition[0].value)
  1106. definition[0].name = definition[0].name + '_disabled'
  1107. def formFill(self,formData):
  1108. """ Fill this editbox with data from the form.
  1109. This is used by intermediate steps (like register serial)
  1110. to remember field values and for refilling a form if an error
  1111. is encountered and the user has to resubmit a form. """
  1112. if not hasattr(editbox,'currentBoxNumber'):
  1113. editbox.currentBoxNumber = 0
  1114. for field,definition in self.fields.items():
  1115. first = True
  1116. numberOfBoxes = None
  1117. if formData.has_key(field):
  1118. if type(formData[field]) is list:
  1119. # Remember how many editboxes this form has
  1120. if first:
  1121. # NB! ASSUMES THAT THE FIRST FIELDNAME IN A FORM
  1122. # IS NEVER A CHECKBOX (SINCE UNCHECKED CHECCKBOXES
  1123. # ARE NOT POSTED). IF IT IS, THEN numberOfBoxes
  1124. # WILL BE ONE LESS THAN IT SHOULD BE FOR EACH
  1125. # UNCHECKED CHECKBOX
  1126. numberOfBoxes = len(formData[field])
  1127. first = False
  1128. # We are editing more than one entry, pick the
  1129. # right data from the form
  1130. definition[0].value = formData[field][editbox.currentBoxNumber]
  1131. else:
  1132. definition[0].value = formData[field]
  1133. # Update class variable currentFormNumber
  1134. if numberOfBoxes:
  1135. editbox.currentBoxNumber +=1
  1136. if editbox.currentBoxNumber == numberOfBoxes:
  1137. # Reset the currentFormNumber class instance
  1138. # Since it is a class instance, it will be common
  1139. # to all editbox instances for all requests, that's
  1140. # why it has to be reset
  1141. editbox.currentBoxNumber = 0
  1142. class editboxHiddenOrMessage(editbox):
  1143. """ This editbox can display a message and contain hidden inputs. """
  1144. page = 'hiddenormessage'
  1145. def __init__(self,message=None):
  1146. self.hiddenFields = {}
  1147. self.message = message
  1148. # The editboxNetbox has UPDATE_ENTRY (which holds the id) or ADDNEW,
  1149. # don't need to repeat it here (so setting boxname to IGNORE_BOX)
  1150. self.boxName = IGNORE_BOX
  1151. class seeddbPage:
  1152. """ The main editing class. Every edit page inherits from this class.
  1153. Contains functions for adding, updating and describing entries.
  1154. Default functions can be overriden by children to do more specific
  1155. handling of adding or updating.
  1156. The children of this class contains all the information needed
  1157. for handling the different tables.
  1158. class seeddbPage
  1159. |+-- class listDef
  1160. |+-- class editbox
  1161. |+-- (optionally more editboxes)
  1162. |+-- def add
  1163. |+-- def update
  1164. |+-- def delete
  1165. |+-- def describe
  1166. """
  1167. def add(self,req,outputForm,action):
  1168. """ Called when 'Add' is clicked on an edit page.
  1169. Takes the formdata from the request object and inserts
  1170. an entry in the database.
  1171. This is the general function used by almost all the edit
  1172. pages. Some of the edit page classes overrides this function
  1173. for more control over the adding (eg. the netbox page).
  1174. req: request object containing a form
  1175. outputForm: form with editboxes
  1176. manipulated directly by eg. editNetbox.add() """
  1177. error = None
  1178. status = seeddbStatus()
  1179. id = None
  1180. nextId = None
  1181. if self.sequence:
  1182. # Get next id from sequence, will need this id to reload
  1183. # entry when making a description of the inserted row
  1184. # In the case that sequence=None, idfield is already
  1185. # present in the field data
  1186. sql = "SELECT nextval('%s')" % (self.sequence,)
  1187. result = executeSQLreturn(sql)
  1188. nextId = str(result[0][0])
  1189. sql = 'INSERT INTO ' + self.tableName + ' ('
  1190. first = True
  1191. for field,descr in self.fields.items():
  1192. if req.form.has_key(field):
  1193. if len(req.form[field]):
  1194. if not first:
  1195. sql += ','
  1196. sql += field
  1197. first = False
  1198. # Add the idfield if we have queried the sequence
  1199. if nextId:
  1200. sql += "," + self.tableIdKey
  1201. sql += ') VALUES ('
  1202. first = True
  1203. for field,descr in self.fields.items():
  1204. if req.form.has_key(field):
  1205. if len(req.form[field]):
  1206. if not first:
  1207. sql += ','
  1208. sql += "'" + req.form[field] + "'"
  1209. first = False
  1210. # Add the id value if we have queried the sequence
  1211. if nextId:
  1212. sql += ",'" + nextId + "'"
  1213. sql += ')'
  1214. try:
  1215. executeSQL([sql])
  1216. except psycopg2.IntegrityError, e:
  1217. rollbackSQL()
  1218. if type(self.unique) is list:
  1219. error = 'There already exists an entry with '
  1220. first = True
  1221. for field in self.unique:
  1222. if not first:
  1223. error += ' and '
  1224. error += field + "='" + req.form[field] + "'"
  1225. first = False
  1226. else:
  1227. error = "There already exists an entry with the value '" + \
  1228. req.form[self.unique] + "' for the unique field '" +\
  1229. self.unique + "'"
  1230. if error:
  1231. status.errors.append(error)
  1232. else:
  1233. if self.sequence:
  1234. id = nextId
  1235. else:
  1236. id = req.form[self.tableIdKey]
  1237. entry = self.table(id)
  1238. message = 'Added ' + self.singular + ': ' + self.describe(entry)
  1239. status.messages.append(message)
  1240. action = 'list'
  1241. return (status,action,outputForm,id)
  1242. def update(self,req,outputForm,selected):
  1243. """ Updates one or more entries in the database. Takes data
  1244. from form in request object. Overriden by some subclasses
  1245. such as pageNetbox. """
  1246. status = seeddbStatus()
  1247. sqllist = []
  1248. data = []
  1249. error = None
  1250. # Get the name of one of the fields that should be present
  1251. presentfield = self.fields.keys()[0]
  1252. # Use this field to check if there are multiple
  1253. # editboxes (multiple entries edited)
  1254. if type(req.form[presentfield]) is list:
  1255. for i in range(0,len(req.form[presentfield])):
  1256. values = {}
  1257. for field,descr in self.fields.items():
  1258. # Special case: checkboxes are not posted if
  1259. # they are not selected. Check if the field
  1260. # is present with "req.form[field][i] and
  1261. # catch the indexError exception if it's not
  1262. # present.
  1263. try:
  1264. req.form[field][i]
  1265. # Don't insert empty strings into fields
  1266. # where required = REQ_NONEMPTY
  1267. if len(req.form[field][i]):
  1268. values[field] = req.form[field][i]
  1269. else:
  1270. if descr[1] != REQ_NONEMPTY:
  1271. values[field] = req.form[field][i]
  1272. else:
  1273. # Insert NULL instead
  1274. values[field] = None
  1275. except (KeyError,IndexError):
  1276. # Field not present (ie. unchecked checkbox)
  1277. # Insert NULL
  1278. values[field] = None
  1279. # The hidden element UPDATE_ENTRY contains the original ID
  1280. data.append((req.form[UPDATE_ENTRY][i],values))
  1281. else:
  1282. values = {}
  1283. for field,descr in self.fields.items():
  1284. try:
  1285. req.form[field]
  1286. if len(req.form[field]):
  1287. values[field] = req.form[field]
  1288. else:
  1289. # Don't insert empty strings into fields
  1290. # where required = REQ_NONEMPTY
  1291. if descr[1] != REQ_NONEMPTY:
  1292. values[field] = req.form[field]
  1293. else:
  1294. # Insert NULL instead
  1295. values[field] = None
  1296. except KeyError:
  1297. # Field not present (ie. ie unchecked checkbox)
  1298. values[field] = None
  1299. # The hidden element UPDATE_ENTRY contains the original ID
  1300. data.append((req.form[UPDATE_ENTRY],values))
  1301. for i in range(0,len(data)):
  1302. sql = 'UPDATE ' + self.tableName + ' SET '
  1303. id,fields = data[i]
  1304. first = True
  1305. for field,value in fields.items():
  1306. if not first:
  1307. sql += ','
  1308. if value:
  1309. sql += field + "='" + value + "'"
  1310. else:
  1311. sql += field + '=NULL'
  1312. first = False
  1313. sql += ' WHERE ' + self.tableIdKey + "='" + id + "'"
  1314. try:
  1315. executeSQL([sql])
  1316. except psycopg2.IntegrityError:
  1317. # Assumes tableIdKey = the unique field
  1318. rollbackSQL()
  1319. if type(self.unique) is list:
  1320. error = 'There already exists an entry with '
  1321. first = True
  1322. for field in self.unique:
  1323. if not first:
  1324. error += ' and '
  1325. error += field + "='" + req.form[field][i] + "'"
  1326. first = False
  1327. status.errors.append(error)
  1328. else:
  1329. error = "There already exists an entry with the value '" + \
  1330. req.form[self.unique] + \
  1331. "' for the unique field '" + self.unique + "'"
  1332. status.errors.append(error)
  1333. # Make a list of id's. If error is returned then the original
  1334. # id's are still valid, if-not error then id's might have changed
  1335. idlist = []
  1336. if error:
  1337. for i in range(0,len(data)):
  1338. id,fields = data[i]
  1339. idlist.append(id)
  1340. elif not self.editIdAllowed:
  1341. # Id can't be edited by the user, so the ids are the same as
  1342. # we started with
  1343. for i in range(0,len(data)):
  1344. id,fields = data[i]
  1345. idlist.append(id)
  1346. else:
  1347. if type(req.form[self.tableIdKey]) is list:
  1348. for i in range(0,len(req.form[self.tableIdKey])):
  1349. idlist.append(req.form[self.tableIdKey][i])
  1350. else:
  1351. idlist.append(req.form[self.tableIdKey])
  1352. action = 'list'
  1353. if error:
  1354. action = 'edit'
  1355. else:
  1356. # All entries updated without error, make list to displayed
  1357. # in the status header
  1358. for entry in idlist:
  1359. message = "Updated " + self.singular + ": "
  1360. message += self.describe(entry)
  1361. status.messages.append(message)
  1362. return (status,action,outputForm,selected)
  1363. def describe(self,id):
  1364. """ Gives a textual description of a database entry.
  1365. Used when adding, deleting and updating entries to tell the user
  1366. exactly what entries were altered (useful when the id field
  1367. of the table only contains a numerical key).
  1368. If the edit page class contains a descriptionFormat it
  1369. is used. Otherwise it falls back to using the id field
  1370. passed to the function.
  1371. Remember to only use NOT NULL fields when writing descrFormats
  1372. to avoid malformed descriptions. """
  1373. description = ''
  1374. try:
  1375. entry = self.table(id)
  1376. if hasattr(self,'descriptionFormat'):
  1377. # Use the description format for this page to make a
  1378. # nicely formatted description of a database entry
  1379. for part in self.descriptionFormat:
  1380. (string,field) = part
  1381. fieldData = ''
  1382. if field:
  1383. # Get data from a db field
  1384. field = field.split('.')
  1385. if len(field) > 1:
  1386. tempentry = entry
  1387. i=0
  1388. for f in field:
  1389. #if i==1:
  1390. # raise(repr(tempentry)+':'+f)
  1391. i+=1
  1392. tempentry = getattr(tempentry,f)
  1393. if type(tempentry) is str:
  1394. # Got the field we're after
  1395. break
  1396. fieldData = tempentry
  1397. else:
  1398. fieldData = getattr(entry,field[0])
  1399. description += string + str(fieldData)
  1400. else:
  1401. # Vanilla description (only the id field)
  1402. description = "'" + id +"'"
  1403. except forgetSQL.NotFound:
  1404. # No such id in this table
  1405. pass
  1406. return description
  1407. def delete(self,idList,status):
  1408. for id in idList:
  1409. try:
  1410. deletedName = self.describe(id)
  1411. deleteEntry([id],self.tableName,self.tableIdKey)
  1412. status.messages.append("Deleted %s: %s" % \
  1413. (self.singular,deletedName))
  1414. except psycopg2.IntegrityError:
  1415. # Got integrity error while deleting, must check what
  1416. # dependencies are blocking. But firstly, we roll back the
  1417. # failed transaction.
  1418. rollbackSQL()
  1419. error = "Error while deleting %s '%s': " % (self.singular,
  1420. deletedName)
  1421. errorState = False
  1422. for (table,other,key,url) in self.dependencies:
  1423. where = "%s='%s'" % (key,str(id))
  1424. if table.getAll(where):
  1425. errorState = True
  1426. # UGLY.. HTML
  1427. error += "referenced in " + \
  1428. "one or more %s " % (other,) + \
  1429. "<a href=\"%s%s\">" % (url,id) + \
  1430. "(view report)</a>."
  1431. break
  1432. if not errorState:
  1433. # There are no dependencies or couldn't find reference.
  1434. # Give general error
  1435. error += '%s is referenced in another table' % (self.name,)
  1436. status.errors.append(error)
  1437. return status
  1438. class pageCabling(seeddbPage):
  1439. """ Describes editing of the cabling table. """
  1440. basePath = BASEPATH + 'cabling/'
  1441. table = nav.db.manage.Cabling
  1442. pageName = 'cabling'
  1443. tableName = 'cabling'
  1444. tableIdKey = 'cablingid'
  1445. sequence = 'cabling_cablingid_seq'
  1446. editMultipleAllowed = True
  1447. editIdAllowed = False
  1448. # Unique fields (for errormessages from add/update)
  1449. unique = ['roomid','jack']
  1450. # Nouns
  1451. singular = 'cabling'
  1452. plural = 'cablings'
  1453. # Delete dependencies
  1454. dependencies = [(nav.db.manage.Patch,
  1455. 'patches',
  1456. 'cablingid',
  1457. '/report/netbox/?roomid=')]
  1458. # Description format used by describe(id)
  1459. # Example: room 021 (descr) to jack 123, building, office
  1460. descriptionFormat = [('\'jack ','jack'),
  1461. (', ','targetroom'),
  1462. (', ','building'),
  1463. ('\' to room \'','room.roomid'),
  1464. (', ','room.location.locationid'),
  1465. ('\'',None)]
  1466. pathAdd = EDITPATH + [('Cabling',basePath+'list'),('Add',False)]
  1467. pathEdit = EDITPATH + [('Cabling',basePath+'list'),('Edit',False)]
  1468. pathDelete = EDITPATH + [('Cabling',basePath+'list'),('Delete',False)]
  1469. pathList = EDITPATH + [('Cabling',False)]
  1470. class listDef(entryList):
  1471. """ Describes the format of the list view of cablings. """
  1472. def __init__(self,req,struct,sort,deleteWhere=None):
  1473. # Do general init
  1474. entryList.__init__(self,req,struct,sort,deleteWhere)
  1475. # Specific init
  1476. self.defaultSortBy = 1
  1477. # List filters
  1478. self.filters = [['Show cablings in room ',
  1479. ('','Select a room'),
  1480. ('roomid,descr',
  1481. 'room',
  1482. None,
  1483. '((SELECT count(*) FROM cabling WHERE ' +\
  1484. 'cabling.roomid=room.roomid) > 0)',
  1485. 'room.roomid'),
  1486. '{id}',
  1487. '{id} ({descr})',
  1488. 'flt_room',
  1489. 'roomid',
  1490. nav.db.manage.Cabling,
  1491. 'room.roomid']]
  1492. # list of (heading text, show sortlink, compare function for sort)
  1493. self.headingDefinition = [('Select',False,None),
  1494. ('Room',True,None),
  1495. ('Jack',True,None),
  1496. ('Building',True,None),
  1497. ('Target room',True,None),
  1498. ('Category',True,None),
  1499. ('Description',True,None)]
  1500. self.cellDefinition = [(('cablingid,roomid,jack,' + \
  1501. 'building,targetroom,category,descr',
  1502. 'cabling',
  1503. None,
  1504. None,
  1505. 'roomid,jack'),
  1506. [(None,None,entryListCell.CHECKBOX,None,None),
  1507. (1,'{p}edit/{id}',None,None,None),
  1508. (2,None,None,None,None),
  1509. (3,None,None,None,None),
  1510. (4,None,None,None,None),
  1511. (5,None,None,None,None),
  1512. (6,None,None,None,None)])]
  1513. class editbox(editbox):
  1514. """ Describes fields for adding and editing cabling entries.
  1515. The template uses this field information to draw the form. """
  1516. def __init__(self,page,req=None,editId=None,formData=None):
  1517. self.page = page.pageName
  1518. self.table = page.table
  1519. self.hiddenFields = {}
  1520. if editId:
  1521. # Preserve the selected id
  1522. self.addHidden(selectList.cnameChk,editId)
  1523. self.editId = editId
  1524. r = [(None,'Select a room')]
  1525. for room in nav.db.manage.Room.getAllIterator(orderBy='roomid'):
  1526. loc = nav.db.manage.Location(room.location).descr
  1527. r.append((room.roomid,room.roomid + ' (' + loc + ':' + \
  1528. str(room.descr) + ')'))
  1529. # Field definitions {field name: [input object, required]}
  1530. f = {'roomid': [inputSelect(options=r),REQ_TRUE,'Room',
  1531. FIELD_STRING],
  1532. 'jack': [inputText(),REQ_TRUE,'Jack',FIELD_STRING],
  1533. 'building': [inputText(),REQ_TRUE,'Building',FIELD_STRING],
  1534. 'targetroom': [inputText(),REQ_TRUE,'Target room',
  1535. FIELD_STRING],
  1536. 'descr': [inputText(),REQ_FALSE,'Description',FIELD_STRING],
  1537. 'category': [inputSelect(options=CATEGORY_LIST),REQ_TRUE,
  1538. 'Category',FIELD_STRING]}
  1539. self.fields = f
  1540. self.setControlNames()
  1541. # This box is for editing existing with id = editId
  1542. if editId:
  1543. self.editId = editId
  1544. self.fill()
  1545. if formData:
  1546. self.formFill(formData)
  1547. class pageLocation(seeddbPage):
  1548. """ Describes editing of the location table. """
  1549. basePath = BASEPATH + 'location/'
  1550. table = nav.db.manage.Location
  1551. pageName = 'location'
  1552. tableName = 'location'
  1553. tableIdKey = 'locationid'
  1554. sequence = None
  1555. editMultipleAllowed = True
  1556. editIdAllowed = True
  1557. # Description format used by describe(id)
  1558. descriptionFormat = [('','locationid')]
  1559. # Unique fields (for errormessages from add/update)
  1560. unique = 'locationid'
  1561. # Nouns
  1562. singular = 'location'
  1563. plural = 'locations'
  1564. # Delete dependencies
  1565. dependencies = [(nav.db.manage.Room,
  1566. 'rooms',
  1567. 'locationid',
  1568. '/report/room/?locationid=')]
  1569. pathAdd = EDITPATH + [('Location',basePath+'list'),('Add',False)]
  1570. pathEdit = EDITPATH + [('Location',basePath+'list'),('Edit',False)]
  1571. pathDelete = EDITPATH + [('Location',basePath+'list'),('Delete',False)]
  1572. pathList = EDITPATH + [('Location',False)]
  1573. class listDef(entryList):
  1574. """ Describes location list view """
  1575. def __init__(self,req,struct,sort,deleteWhere=None):
  1576. # Do general init
  1577. entryList.__init__(self,req,struct,sort,deleteWhere)
  1578. # Specific init
  1579. self.defaultSortBy = 1
  1580. # list of (heading text, show sortlink, compare function for sort)
  1581. self.headingDefinition = [('Select',False,None),
  1582. ('Location Id',True,None),
  1583. ('Description',True,None)]
  1584. self.cellDefinition = [(('locationid,locationid,descr',
  1585. 'location',
  1586. None,
  1587. None,
  1588. 'locationid'),
  1589. [(None,None,entryListCell.CHECKBOX,None,None),
  1590. (1,'{p}edit/{id}',None,None,None),
  1591. (2,None,None,None,None)])]
  1592. class editbox(editbox):
  1593. """ Describes fields for adding and editing location entries.
  1594. The template uses this field information to draw the form. """
  1595. def __init__(self,page,req=None,editId=None,formData=None):
  1596. self.page = page.pageName
  1597. self.table = page.table
  1598. self.hiddenFields = {}
  1599. disabled = False
  1600. if editId:
  1601. disabled = True
  1602. f = {'locationid': [inputText(disabled=disabled,
  1603. maxlength=30),REQ_TRUE,'Location Id',
  1604. FIELD_STRING],
  1605. 'descr': [inputText(),REQ_TRUE,'Description',
  1606. FIELD_STRING]}
  1607. self.fields = f
  1608. self.setControlNames()
  1609. if editId:
  1610. self.editId = editId
  1611. self.fill()
  1612. if formData:
  1613. self.formFill(formData)
  1614. if disabled:
  1615. self.addDisabled()
  1616. class pageNetbox(seeddbPage):
  1617. """ Describes editing of the netbox table """
  1618. basePath = BASEPATH + 'netbox/'
  1619. table = nav.db.manage.Netbox
  1620. pageName = 'netbox'
  1621. tableName = 'netbox'
  1622. tableIdKey = 'netboxid'
  1623. sequence = 'netbox_netboxid_seq'
  1624. editMultipleAllowed = False
  1625. editIdAllowed = True
  1626. # Nouns
  1627. singular = 'IP device'
  1628. plural = 'IP devices'
  1629. # Delete dependencies
  1630. dependencies = []
  1631. # Description format used by describe(id)
  1632. descriptionFormat = [('','sysname')]
  1633. pathAdd = EDITPATH + [('IP devices',basePath+'list'),('Add',False)]
  1634. pathEdit = EDITPATH + [('IP devices',basePath+'list'),('Edit',False)]
  1635. pathDelete = EDITPATH + [('IP devices',basePath+'list'),('Delete',False)]
  1636. pathList = EDITPATH + [('IP devices',False)]
  1637. class listDef(entryList):
  1638. """ Describes the format of the list view of netboxes. """
  1639. def ipCompare(self,ip1,ip2):
  1640. """ Function for comparing two ip's. Used for sorting the
  1641. netbox list. """
  1642. # ip1[0] and ip2[0] are the sort parameter
  1643. ip1 = ip1[0].split('.')
  1644. ip2 = ip2[0].split('.')
  1645. r = 0
  1646. try:
  1647. for i in range(0,4):
  1648. r = cmp(int(ip1[i]),int(ip2[i]))
  1649. if r != 0:
  1650. break
  1651. except:
  1652. r = 0
  1653. return r
  1654. def __init__(self,req,struct,sort,deleteWhere=None):
  1655. # Do general init
  1656. entryList.__init__(self,req,struct,sort,deleteWhere)
  1657. # Specific init
  1658. # 1 = roomid
  1659. self.defaultSortBy = 1
  1660. # list of (heading text, show sortlink, compare function for sort)
  1661. self.headingDefinition = [('Select',False,None),
  1662. ('Room',True,None),
  1663. ('Sysname',True,None),
  1664. ('IP',True,self.ipCompare),
  1665. ('Category',True,None),
  1666. ('Organisation',True,None),
  1667. ('RO',True,None),
  1668. ('RW',True,None),
  1669. ('Type',True,None),
  1670. ('Serial',True,None)]
  1671. subcatTooltip = [['SELECT n.netboxid, nc.category ' + \
  1672. 'FROM netbox n, netboxcategory nc ' + \
  1673. 'WHERE nc.netboxid=n.netboxid',
  1674. ('Subcategories:','{$1}'),None],
  1675. ['SELECT netboxid,val FROM netboxinfo ' + \
  1676. 'WHERE var=\'function\'',
  1677. ('Function:','{$1}'),None]]
  1678. self.cellDefinition = [(('netboxid,roomid,sysname,ip,' + \
  1679. 'catid,orgid,ro,rw,type.typename,' + \
  1680. 'device.serial',
  1681. 'netbox',
  1682. 'LEFT JOIN type ON ' + \
  1683. 'netbox.typeid=type.typeid LEFT JOIN ' +\
  1684. 'device ON ' + \
  1685. 'netbox.deviceid=device.deviceid',
  1686. None,
  1687. 'roomid,sysname'),
  1688. [(None,None,entryListCell.CHECKBOX,None,None),
  1689. (1,None,None,None,None),
  1690. (2,'{p}edit/{id}',None,None,None),
  1691. (3,None,None,None,None),
  1692. (4,None,None,None,subcatTooltip),
  1693. (5,None,None,None,None),
  1694. (6,None,None,None,None),
  1695. (7,None,None,None,None),
  1696. (8,None,None,None,None),
  1697. (9,None,None,None,None)])]
  1698. class editbox(editbox):
  1699. """ This is the first editbox on the edit netbox page. It asks
  1700. for ip,ro,rw,cat,org and room. """
  1701. def __init__(self,page,req=None,editId=None,formData=None,
  1702. disabled=False):
  1703. self.editId = editId
  1704. self.page = page.pageName
  1705. self.table = page.table
  1706. self.hiddenFields = {}
  1707. if editId:
  1708. # Preserve the selected id
  1709. self.addHidden(selectList.cnameChk,editId)
  1710. self.sysname = manage.Netbox(editId).sysname
  1711. self.editId = editId
  1712. self.path = EDITPATH + [('IP devices','/seeddb/netbox/list'),
  1713. ('Edit',False)]
  1714. else:
  1715. self.path = EDITPATH + [('IP devices','/seeddb/netbox/list'),
  1716. ('Add',False)]
  1717. o = [(None,'Select an organisation')]
  1718. for org in manage.Org.getAllIterator(orderBy='orgid'):
  1719. o.append((org.orgid,org.orgid + ' (' + str(org.descr) + ')'))
  1720. r = [(None,'Select a room')]
  1721. for room in manage.Room.getAllIterator(orderBy='roomid'):
  1722. loc = manage.Location(room.location).descr
  1723. r.append((room.roomid,room.roomid + ' (' + str(loc) + ':' + \
  1724. str(room.descr) + ')'))
  1725. c = [(None,'Select a category')]
  1726. for cat in manage.Cat.getAllIterator(orderBy='catid'):
  1727. c.append((cat.catid,cat.catid + ' (' + str(cat.descr) + ')'))
  1728. # Field definitions {field name: [input object, required]}
  1729. f = {'ip': [inputText(disabled=disabled),REQ_TRUE,'IP or hostname',
  1730. FIELD_STRING],
  1731. 'catid': [inputSelect(options=c,disabled=disabled),REQ_TRUE,
  1732. 'Category',FIELD_STRING],
  1733. 'orgid': [inputSelect(options=o,disabled=disabled),REQ_TRUE,
  1734. 'Organisation',FIELD_STRING],
  1735. 'roomid': [inputSelect(options=r,disabled=disabled),REQ_TRUE,
  1736. 'Room',FIELD_STRING],
  1737. 'ro': [inputText(disabled=disabled),REQ_FALSE,
  1738. 'RO community',FIELD_STRING],
  1739. 'rw': [inputText(disabled=disabled),REQ_FALSE,
  1740. 'RW community',FIELD_STRING]}
  1741. self.fields = f
  1742. self.setControlNames()
  1743. if editId:
  1744. # This box is for editing an existing netbox with id = editId
  1745. self.editId = editId
  1746. self.fill()
  1747. if formData:
  1748. self.formFill(formData)
  1749. if disabled:
  1750. self.addDisabled()
  1751. class editboxSerial(editbox):
  1752. """ This editbox for inputing serials (or showing a serial if
  1753. it was found by snmp). """
  1754. page = 'netboxserial'
  1755. def __init__(self,gotRo,serial='',sysname=None,typeid=None,
  1756. snmpversion=None,formData=None,editSerial=False):
  1757. self.hiddenFields = {}
  1758. # Set info fields
  1759. self.sysname = sysname
  1760. if typeid:
  1761. self.typename = manage.Type(typeid).typename
  1762. else:
  1763. self.typename = 'n/a'
  1764. if snmpversion:
  1765. self.snmpversion = snmpversion
  1766. else:
  1767. self.snmpversion = 'n/a'
  1768. disabled = False
  1769. self.help = None
  1770. if gotRo:
  1771. # RO was specified, so the box has been queried by SNMP
  1772. if serial:
  1773. # It returned a serialnumber
  1774. disabled = True
  1775. self.help = 'Serialnumber retrieved by SNMP.'
  1776. else:
  1777. self.help = 'Unable to retrieve serialnumber for this ' + \
  1778. 'device by SNMP. ' + \
  1779. 'Enter a serialnumber (optional).'
  1780. else:
  1781. if serial:
  1782. # Serial was entered manually
  1783. self.help = ''
  1784. disabled = True
  1785. else:
  1786. self.help = 'Enter a serialnumber (optional).'
  1787. # If editSerial = True, override help text and always enable editing
  1788. if editSerial:
  1789. disabled = False
  1790. # Should be some help text here
  1791. self.help = ''
  1792. self.fields = {'serial': [inputText(value=serial,disabled=disabled),
  1793. REQ_FALSE,'Serialnumber',FIELD_STRING]}
  1794. self.setControlNames()
  1795. self.addHidden('sysname',sysname)
  1796. self.addHidden('typeid',typeid)
  1797. self.addHidden('snmpversion',snmpversion)
  1798. if formData:
  1799. self.formFill(formData)
  1800. if disabled:
  1801. self.addDisabled()
  1802. # The editboxNetbox has UPDATE_ENTRY (which holds the id) or ADDNEW,
  1803. # don't need to repeat it here
  1804. self.boxName = IGNORE_BOX
  1805. class editboxCategory(editbox):
  1806. """ This editbox is for editing function and subcategories. """
  1807. page = 'netboxcategory'
  1808. def __init__(self,catid,editId=None,showHelp=True):
  1809. self.hiddenFields = {}
  1810. subcategories = False
  1811. if len(manage.Subcat.getAll(where="catid='" + catid + "'")):
  1812. subcategories = True
  1813. self.help = None
  1814. if editId:
  1815. self.editId = editId
  1816. elif showHelp:
  1817. # Only show help if we're adding a new box
  1818. if subcategories:
  1819. self.help = 'You can select one or more subcategories ' +\
  1820. 'for IP devices with the selected category. ' +\
  1821. 'You can also add an optional description ' +\
  1822. 'of the function of this box.'
  1823. else:
  1824. self.help = 'You can add an optional description of the '+\
  1825. 'function of this IP device.'
  1826. o = []
  1827. for subcat in manage.Subcat.getAllIterator(where="catid='" + \
  1828. catid + "'"):
  1829. o.append((subcat.subcatid,subcat.subcatid + ' (' + \
  1830. subcat.descr + ')'))
  1831. if editId:
  1832. if subcategories:
  1833. self.fields = {'subcat': \
  1834. [inputMultipleSelect(options=o),REQ_FALSE,
  1835. 'Subcategory',FIELD_STRING],
  1836. 'function': [inputText(size=40),REQ_FALSE,
  1837. 'Function',FIELD_STRING]}
  1838. else:
  1839. self.fields = {'function': [inputText(size=40),REQ_FALSE,
  1840. 'Function',FIELD_STRING]}
  1841. else:
  1842. if subcategories:
  1843. self.fields = {'subcat': [inputMultipleSelect(options=o),
  1844. REQ_FALSE,'Subcategory',
  1845. FIELD_STRING],
  1846. 'function': [inputText(size=40),REQ_FALSE,
  1847. 'Function',FIELD_STRING]}
  1848. else:
  1849. self.fields = {'function': [inputText(size=40),REQ_FALSE,
  1850. 'Function',FIELD_STRING]}
  1851. self.setControlNames()
  1852. if editId:
  1853. # Get selected netboxcategories
  1854. sql = "SELECT category FROM netboxcategory WHERE " +\
  1855. "netboxid='%s'" \
  1856. % (editId,)
  1857. res = executeSQLreturn(sql)
  1858. selected = []
  1859. for s in res:
  1860. selected.append(s[0])
  1861. if subcategories:
  1862. # A subcat field is present for this box with this cat
  1863. self.fields['subcat'][0].value = selected
  1864. # Get var 'function' from netboxinfo
  1865. sql = "SELECT val FROM netboxinfo WHERE netboxid='%s' " \
  1866. % (editId,) + \
  1867. "AND var='function'"
  1868. res = executeSQLreturn(sql)
  1869. if res:
  1870. self.fields['function'][0].value = res[0][0]
  1871. # The editboxNetbox has UPDATE_ENTRY (which holds the id),
  1872. # don't need to repeat it here
  1873. self.boxName = IGNORE_BOX
  1874. # Overrides default add function
  1875. def add(self,req,templateform,action):
  1876. """ Adds a netbox. Overrides the default add function. """
  1877. ADD_TYPE_URL = BASEPATH + 'type/edit'
  1878. STEP_1 = 1
  1879. STEP_2 = 2
  1880. CNAME_STEP = 'step'
  1881. # Step0: ask for ip,ro,rw,catid,org,room
  1882. # Step1: ask for serial (and sysname,snmpversion and typeid)
  1883. # and ask for subcategory and function
  1884. # Step2: add the box
  1885. message = "Got SNMP response, but can't find type in " + \
  1886. "database. You must <a href=\"" + ADD_TYPE_URL + \
  1887. "?sysobjectid=%s\" " + \
  1888. "target=\"_blank\">add the " + \
  1889. "type</a> before proceeding (a new window will " + \
  1890. "open, when the new type is added, press " + \
  1891. "Continue to proceed)."
  1892. box = None
  1893. error = None
  1894. status = seeddbStatus()
  1895. action = 'predefined'
  1896. form = req.form
  1897. templateform.title = 'Add IP device'
  1898. # Add editbox with hidden values for step (and deviceid)
  1899. editboxHidden = editboxHiddenOrMessage()
  1900. templateform.add(editboxHidden)
  1901. # What step are we in?
  1902. step = STEP_1
  1903. if form.has_key(CNAME_STEP):
  1904. step = int(form[CNAME_STEP])
  1905. nextStep = step + 1
  1906. if step == STEP_1:
  1907. # Look up sysname in DNS
  1908. validIP = nav.util.isValidIP(form['ip'])
  1909. ip = form['ip']
  1910. if validIP:
  1911. ip = validIP
  1912. # This is an IP
  1913. try:
  1914. sysname = gethostbyaddr(ip)[0]
  1915. except:
  1916. sysname = ip
  1917. else:
  1918. # Not an IP, possibly a hostname
  1919. try:
  1920. ip = gethostbyname(form['ip'])
  1921. sysname = form['ip']
  1922. except:
  1923. error = 'Invalid IP or hostname'
  1924. # 'ip' should be numerical ip now
  1925. editboxHidden.addHidden('hiddenIP',str(ip))
  1926. # Check if sysname or ip is already present in db
  1927. if not error:
  1928. where = "ip = '" + ip + "'"
  1929. box = manage.Netbox.getAll(where)
  1930. if box:
  1931. box = box[0]
  1932. error = 'IP already exists in database (' + box.sysname + ')'
  1933. else:
  1934. # If IP isn't duplicate, check sysname
  1935. where = "sysname = '" + sysname + "'"
  1936. box = manage.Netbox.getAll(where)
  1937. if box:
  1938. error = 'Sysname ' + sysname + ' (' + box[0].ip + \
  1939. ') already exists in database'
  1940. if error:
  1941. status.errors.append(error)
  1942. templateform.add(pageNetbox.editbox(pageNetbox,formData=form))
  1943. return (status,action,templateform,None)
  1944. if manage.Cat(form['catid']).req_snmp == True:
  1945. # SNMP required by the selected category
  1946. if len(form['ro']):
  1947. # RO specified, check SNMP
  1948. box = None
  1949. try:
  1950. box = initBox.Box(ip,form['ro'])
  1951. except nav.Snmp.TimeOutException:
  1952. # No SNMP response
  1953. status.errors.append('No SNMP response, check RO ' +\
  1954. 'community')
  1955. templateform.add(pageNetbox.editbox(pageNetbox,
  1956. formData=form))
  1957. return (status,action,templateform,None)
  1958. except Exception, e:
  1959. # Other error (no route to host for example)
  1960. status.errors.append('Error: ' + str(sys.exc_info()[0]) + \
  1961. ': ' + str(sys.exc_info()[1]))
  1962. templateform.add(pageNetbox.editbox(pageNetbox,
  1963. formData=form))
  1964. return (status,action,templateform,None)
  1965. box.getDeviceId()
  1966. templateform.add(pageNetbox.editbox(pageNetbox,
  1967. formData=form,
  1968. disabled=True))
  1969. if box.typeid:
  1970. # Got type (required for these categories)
  1971. templateform.add(pageNetbox.editboxSerial(
  1972. gotRo=True,
  1973. serial=box.serial,
  1974. sysname=sysname,
  1975. typeid=box.typeid,
  1976. snmpversion=box.snmpversion))
  1977. templateform.add(pageNetbox.editboxCategory(
  1978. req.form['catid']))
  1979. else:
  1980. # Couldn't find type, ask user to add
  1981. # (type is required for this category)
  1982. message = message % (box.sysobjectid,)
  1983. templateform.add(editboxHiddenOrMessage(message))
  1984. nextStep = STEP_1
  1985. else:
  1986. # RO blank, return error
  1987. status.errors.append('Category ' + form['catid'] + \
  1988. ' requires an RO community')
  1989. templateform.add(pageNetbox.editbox(pageNetbox,
  1990. formData=form))
  1991. nextStep = STEP_1
  1992. else:
  1993. # SNMP not required by cat
  1994. message = "Got SNMP response, but can't find type in " + \
  1995. "database. Type is not required for this " +\
  1996. "category, but if you want you can "+\
  1997. "<a href=\"" + ADD_TYPE_URL + \
  1998. "?sysobjectid=%s\" " + \
  1999. "target=\"_blank\">" + \
  2000. "add this type to the database</a>. " +\
  2001. "After adding the type, start the registration " +\
  2002. "again to set correct type on this IP device."
  2003. if len(form['ro']):
  2004. # RO specified, check SNMP anyway
  2005. box = None
  2006. try:
  2007. box = initBox.Box(ip,form['ro'])
  2008. except nav.Snmp.TimeOutException:
  2009. status.errors.append('No SNMP response, check RO community')
  2010. templateform.add(pageNetbox.editbox(pageNetbox,
  2011. formData=form))
  2012. return (status,action,templateform,None)
  2013. except Exception, e:
  2014. # Other error (no route to host for example)
  2015. status.errors.append('Error: ' + str(sys.exc_info()[0]) + \
  2016. ': ' + str(sys.exc_info()[1]))
  2017. templateform.add(pageNetbox.editbox(pageNetbox,
  2018. formData=form))
  2019. return (status,action,templateform,None)
  2020. box.getDeviceId()
  2021. templateform.add(pageNetbox.editbox(pageNetbox,
  2022. formData=form,
  2023. disabled=True))
  2024. if not box.typeid:
  2025. # Unknown type. Type is not required,
  2026. # but ask if user wants to add type anyway.
  2027. message = message % (box.sysobjectid,)
  2028. templateform.add(editboxHiddenOrMessage(message))
  2029. templateform.add(pageNetbox.editboxSerial(gotRo=True,
  2030. serial=box.serial,
  2031. sysname=sysname,
  2032. typeid=box.typeid,
  2033. snmpversion=box.snmpversion))
  2034. templateform.add(pageNetbox.editboxCategory(
  2035. req.form['catid']))
  2036. else:
  2037. # RO blank, don't check SNMP, ask for serial
  2038. # and subcat+function
  2039. templateform.add(pageNetbox.editbox(pageNetbox,
  2040. formData=form,
  2041. disabled=True))
  2042. templateform.add(pageNetbox.editboxSerial(gotRo=False,
  2043. sysname=sysname))
  2044. templateform.add(pageNetbox.editboxCategory(
  2045. req.form['catid']))
  2046. nextStep = STEP_2
  2047. if step == STEP_2:
  2048. # If we have serial, check if the device is already
  2049. # present in the databse
  2050. deviceId = None
  2051. serial = req.form['serial']
  2052. if len(serial):
  2053. # Any devices in the database with this serial?
  2054. where = "serial = '" + str(serial) + "'"
  2055. device = manage.Device.getAll(where)
  2056. if device:
  2057. # Found a device with this serial
  2058. deviceId = str(device[0].deviceid)
  2059. # Must check if there already is a box with this serial
  2060. where = "deviceid = '" + deviceId + "'"
  2061. box = manage.Netbox.getAll(where)
  2062. if box:
  2063. # A box with this serial already exists
  2064. # in the database. Ask for serial again.
  2065. box = box[0]
  2066. status.errors.append('An IP device (' + box.sysname + \
  2067. ') with the serial \'' +\
  2068. str(serial) +\
  2069. '\' already exists.')
  2070. templateform.add(pageNetbox.editbox(pageNetbox,
  2071. formData=form,
  2072. disabled=True))
  2073. templateform.showConfirm = True
  2074. templateform.add(pageNetbox.editboxSerial(
  2075. gotRo=False,
  2076. sysname=
  2077. req.form['sysname']))
  2078. templateform.add(pageNetbox.editboxCategory(
  2079. req.form['catid']))
  2080. # Ask for serial again. Must "refresh" hidden values.
  2081. nextStep = STEP_2
  2082. editboxHidden.addHidden(CNAME_STEP,nextStep)
  2083. editboxHidden.addHidden('hiddenIP',form['hiddenIP'])
  2084. return (status,action,templateform,None)
  2085. else:
  2086. # Not found, make new device
  2087. deviceId = None
  2088. # Get selected subcategories
  2089. subcatlist = None
  2090. if form.has_key('subcat'):
  2091. subcatlist = form['subcat']
  2092. # Get function
  2093. function = None
  2094. if form.has_key('function'):
  2095. function = form['function']
  2096. # Get typeid and snmpversion (from hidden inputs)
  2097. typeId = None
  2098. if form.has_key('typeid'):
  2099. typeId = form['typeid']
  2100. snmpversion = None
  2101. if form.has_key('snmpversion'):
  2102. snmpversion = form['snmpversion']
  2103. # Get sysname (from hidden input)
  2104. sysname = req.form['sysname']
  2105. # Insert netbox
  2106. # hiddenIP contains numerical ip after dns lookup
  2107. # (in case user entered a hostname in the ip field)
  2108. insertNetbox(form['hiddenIP'],form['sysname'],
  2109. form['catid'],form['roomid'],
  2110. form['orgid'],form['ro'],
  2111. form['rw'],deviceId,
  2112. form['serial'],typeId,
  2113. snmpversion,subcatlist,
  2114. function)
  2115. action = 'list'
  2116. status.messages.append('Added IP device: ' + form['sysname'] + ' (' + \
  2117. req.form['hiddenIP'] + ')')
  2118. if not step == STEP_2:
  2119. # Unless this is the last step, set the nextStep
  2120. editboxHidden.addHidden(CNAME_STEP,nextStep)
  2121. return (status,action,templateform,None)
  2122. # Overloads default update function
  2123. def update(self,req,templateform,selected):
  2124. """ Updates a netbox, overrides the default update function. """
  2125. selected = selected[0]
  2126. ADD_TYPE_URL = BASEPATH + 'type/edit'
  2127. STEP_1 = 1
  2128. STEP_2 = 2
  2129. CNAME_STEP = 'step'
  2130. # Step0: ask for ip,ro,rw,catid,org,room
  2131. # Step1: ask for serial (and sysname,snmpversion and typeid)
  2132. # ask for subcategory and function
  2133. # Step2: update the box
  2134. message = "Got SNMP response, but can't find type in " + \
  2135. "database. You must <a href=\"" + ADD_TYPE_URL + \
  2136. "?sysobjectid=%s\" " + \
  2137. "target=\"_blank\">add the " + \
  2138. "type</a> before proceeding (a new window will " + \
  2139. "open, when the new type is added, press " + \
  2140. "Continue to proceed)."
  2141. box = None
  2142. error = None
  2143. status = seeddbStatus()
  2144. action = 'predefined'
  2145. form = req.form
  2146. templateform.title = 'Edit IP device'
  2147. # Preserve the URL
  2148. templateform.action = BASEPATH + 'netbox/edit/' + selected
  2149. # Add editbox with hidden values for step (and deviceid)
  2150. editboxHidden = editboxHiddenOrMessage()
  2151. templateform.add(editboxHidden)
  2152. # What step are we in?
  2153. step = STEP_1
  2154. if form.has_key(CNAME_STEP):
  2155. step = int(form[CNAME_STEP])
  2156. nextStep = step + 1
  2157. oldBox = seeddbTables.seeddbNetbox(selected)
  2158. if step == STEP_1:
  2159. # Look up sysname in DNS, it might have changed
  2160. # since the box was initially added
  2161. validIP = nav.util.isValidIP(form['ip'])
  2162. ip = form['ip']
  2163. if validIP:
  2164. ip = validIP
  2165. # This is an IP
  2166. try:
  2167. sysname = gethostbyaddr(ip)[0]
  2168. except:
  2169. sysname = ip
  2170. else:
  2171. # Not an IP, possibly a hostname
  2172. try:
  2173. ip = gethostbyname(form['ip'])
  2174. sysname = form['ip']
  2175. except:
  2176. error = 'Invalid IP or hostname'
  2177. # 'ip' should be numerical ip now
  2178. editboxHidden.addHidden('hiddenIP',str(ip))
  2179. # Check if (edited) ip is already present in db
  2180. #if (oldBox.ip != form['ip'])
  2181. if oldBox.ip != ip and (not error):
  2182. # If IP differs from the old, check for uniqueness
  2183. where = "ip = '" + ip + "'"
  2184. box = manage.Netbox.getAll(where)
  2185. if box:
  2186. error = 'IP already exists in database'
  2187. if oldBox.sysname != sysname and not error:
  2188. # If IP isn't duplicate, check if (new) sysname is unique
  2189. where = "sysname = '" + sysname + "'"
  2190. box = manage.Netbox.getAll(where)
  2191. if box:
  2192. error = 'Sysname ' + sysname + ' (' + box[0].ip + \
  2193. ') already exists in database'
  2194. if error:
  2195. status.errors.append(error)
  2196. templateform.add(pageNetbox.editbox(pageNetbox,
  2197. editId=selected,
  2198. formData=form))
  2199. return (status,action,templateform,selected)
  2200. if manage.Cat(form['catid']).req_snmp == True:
  2201. # SNMP required by this category
  2202. if len(form['ro']):
  2203. # RO specified, check SNMP
  2204. box = None
  2205. try:
  2206. box = initBox.Box(form['ip'],form['ro'])
  2207. except nav.Snmp.TimeOutException:
  2208. # No SNMP answer
  2209. status.errors.append('No SNMP response, check ' +\
  2210. 'RO community')
  2211. templateform.add(pageNetbox.editbox(pageNetbox,
  2212. editId=selected,
  2213. formData=form))
  2214. return (status,action,templateform,selected)
  2215. except Exception, e:
  2216. # Other error (no route to host for example)
  2217. status.errors.append('Error: '+str(sys.exc_info()[0])+\
  2218. ': ' + str(sys.exc_info()[1]))
  2219. templateform.add(pageNetbox.editbox(pageNetbox,
  2220. editId=selected,
  2221. formData=form))
  2222. return (status,action,templateform,selected)
  2223. box.getDeviceId()
  2224. templateform.add(pageNetbox.editbox(pageNetbox,
  2225. editId=selected,
  2226. formData=form,
  2227. disabled=True))
  2228. if box.typeid:
  2229. # Got type
  2230. if box.serial:
  2231. serial = box.serial
  2232. else:
  2233. serial = oldBox.device.serial
  2234. templateform.add(pageNetbox.editboxSerial(
  2235. gotRo=True,
  2236. serial=serial,
  2237. sysname=sysname,
  2238. typeid=box.typeid,
  2239. snmpversion=box.snmpversion,
  2240. editSerial=False))
  2241. # Show subcategory/function editbox
  2242. # If category has changed, then don't
  2243. # load the old subcatinfo
  2244. if oldBox.catid != form['catid']:
  2245. templateform.add(pageNetbox.editboxCategory(
  2246. req.form['catid'],
  2247. showHelp=False))
  2248. else:
  2249. templateform.add(pageNetbox.editboxCategory(
  2250. req.form['catid'],
  2251. selected))
  2252. else:
  2253. # Couldn't find type, ask user to add
  2254. message = message % (box.sysobjectid,)
  2255. templateform.add(editboxHiddenOrMessage(message))
  2256. else:
  2257. # RO blank, return error
  2258. status.errors.append('Category ' + form['catid'] + \
  2259. ' requires a RO community')
  2260. templateform.add(pageNetbox.editbox(pageNetbox,
  2261. editId=selected,
  2262. formData=form))
  2263. nextStep = STEP_1
  2264. else:
  2265. # SNMP not required by cat
  2266. message = "Got SNMP response, but can't find type in " + \
  2267. "database. Type is not required for this " +\
  2268. "category, but if you want you can "+\
  2269. "<a href=\"" + ADD_TYPE_URL + \
  2270. "?sysobjectid=%s\" " + \
  2271. "target=\"_blank\">" + \
  2272. "add this type to the database</a>. " +\
  2273. "After adding the type, start the registration " +\
  2274. "again to set correct type on IP device."
  2275. if len(form['ro']):
  2276. # RO specified, check SNMP anyway
  2277. box = None
  2278. try:
  2279. box = initBox.Box(form['ip'],form['ro'])
  2280. except nav.Snmp.TimeOutException:
  2281. status.errors.append('Error: ' + str(sys.exc_info()[0]) + \
  2282. ': ' + str(sys.exc_info()[1]))
  2283. templateform.add(pageNetbox.editbox(pageNetbox,
  2284. editId=selected,
  2285. formData=form))
  2286. return (status,action,templateform,selected)
  2287. except Exception, e:
  2288. # Other error (no route to host for example)
  2289. status.errors.append('Error: '+str(sys.exc_info()[0])+\
  2290. ': ' + str(sys.exc_info()[1]))
  2291. templateform.add(pageNetbox.editbox(pageNetbox,
  2292. editId=selected,
  2293. formData=form))
  2294. return (status,action,templateform,selected)
  2295. box.getDeviceId()
  2296. templateform.add(pageNetbox.editbox(pageNetbox,
  2297. editId=selected,
  2298. formData=form,
  2299. disabled=True))
  2300. if not box.typeid:
  2301. # Unknown type. Type is not required,
  2302. # but ask if user wants to add type anyway.
  2303. message = message % (box.sysobjectid,)
  2304. templateform.add(editboxHiddenOrMessage(message))
  2305. if box.serial:
  2306. serial = box.serial
  2307. else:
  2308. serial = oldBox.device.serial
  2309. templateform.add(pageNetbox.editboxSerial(gotRo=True,
  2310. serial=serial,
  2311. sysname=sysname,
  2312. typeid=box.typeid,
  2313. snmpversion=box.snmpversion,
  2314. editSerial=False))
  2315. # Show subcategory/function editbox
  2316. # If category has changed, then don't
  2317. # load the old subcatinfo
  2318. if oldBox.catid != form['catid']:
  2319. templateform.add(pageNetbox.editboxCategory(
  2320. req.form['catid'],
  2321. showHelp=False))
  2322. else:
  2323. templateform.add(pageNetbox.editboxCategory(
  2324. req.form['catid'],
  2325. selected))
  2326. else:
  2327. # RO blank, don't check SNMP, ask for serial
  2328. templateform.add(pageNetbox.editbox(pageNetbox,
  2329. editId=selected,
  2330. formData=form,
  2331. disabled=True))
  2332. serial = oldBox.device.serial
  2333. templateform.add(pageNetbox.editboxSerial(gotRo=False,
  2334. serial = serial,
  2335. sysname=sysname,
  2336. editSerial=True))
  2337. # Show subcategory/function editbox
  2338. # If category has changed, then don't
  2339. # load the old subcatinfo
  2340. if oldBox.catid != form['catid']:
  2341. templateform.add(pageNetbox.editboxCategory(
  2342. req.form['catid'],
  2343. showHelp=False))
  2344. else:
  2345. templateform.add(pageNetbox.editboxCategory(
  2346. req.form['catid'],
  2347. selected))
  2348. nextStep = STEP_2
  2349. if step == STEP_2:
  2350. # Always use the old serial
  2351. serial = oldBox.device.serial
  2352. # If the serial was changed we have to check if it's unique
  2353. if box:
  2354. # Got serial by SNMP?
  2355. newSerial = box.serial
  2356. else:
  2357. newSerial = form['serial']
  2358. if len(newSerial):
  2359. deviceId = None
  2360. if serial != newSerial:
  2361. # Any other devices in the database with this serial?
  2362. where = "serial = '" + str(newSerial) + "'"
  2363. device = manage.Device.getAll(where)
  2364. if device:
  2365. # Found a device with this serial
  2366. deviceId = str(device[0].deviceid)
  2367. # Must check if there already is a box with this serial
  2368. where = "deviceid = '" + deviceId + "'"
  2369. box = manage.Netbox.getAll(where)
  2370. if box:
  2371. # A box with this serial already exists
  2372. # in the database. Ask for serial again.
  2373. box = box[0]
  2374. status.errors.append('An IP device (' + box.sysname + \
  2375. ') with the serial \'' +\
  2376. str(serial) +\
  2377. '\' already exists.')
  2378. templateform.add(pageNetbox.editbox(pageNetbox,
  2379. formData=form,
  2380. disabled=True))
  2381. templateform.showConfirm = True
  2382. templateform.add(pageNetbox.editboxSerial(
  2383. gotRo=False,
  2384. sysname=
  2385. req.form['sysname']))
  2386. templateform.add(pageNetbox.editboxCategory(
  2387. req.form['catid']))
  2388. # Ask for serial again. Must "refresh" hidden values.
  2389. nextStep = STEP_2
  2390. editboxHidden.addHidden(CNAME_STEP,nextStep)
  2391. editboxHidden.addHidden('hiddenIP',form['hiddenIP'])
  2392. #templateform.add(editboxHiddenOrMessage(message))
  2393. return (status,action,templateform,selected)
  2394. else:
  2395. # No serial, make new device
  2396. deviceId = None
  2397. # Get selected subcats, function, type and snmpversion
  2398. subcatlist = None
  2399. if form.has_key('subcat'):
  2400. subcatlist = form['subcat']
  2401. if not type(subcatlist) is list:
  2402. subcatlist = [subcatlist]
  2403. function = None
  2404. if form.has_key('function'):
  2405. function = req.form['function']
  2406. typeId = None
  2407. if form.has_key('typeid'):
  2408. typeId = req.form['typeid']
  2409. snmpversion = None
  2410. if form.has_key('snmpversion'):
  2411. snmpversion = form['snmpversion']
  2412. # Only use first char of snmpversion, don't insert things like
  2413. # '2c'
  2414. if len(snmpversion):
  2415. snmpversion = snmpversion[0]
  2416. # Update netbox
  2417. fields = {'ip': form['hiddenIP'],
  2418. 'sysname': form['sysname'],
  2419. 'catid': form['catid'],
  2420. 'roomid': form['roomid'],
  2421. 'orgid': form['orgid'],
  2422. 'ro': form['ro'],
  2423. 'rw': form['rw']}
  2424. # Set type if initbox found it
  2425. if typeId:
  2426. fields['typeid'] = typeId
  2427. # Update deviceid if it has changed (ie. if serial
  2428. # was updated and a device with this serial already
  2429. # existed in the database
  2430. if deviceId:
  2431. fields['deviceid'] = deviceId
  2432. # Get prefixid
  2433. query = "SELECT prefixid FROM prefix WHERE '%s'::inet << netaddr" \
  2434. % (fields['ip'],)
  2435. try:
  2436. result = executeSQLreturn(query)
  2437. fields['prefixid'] = str(result[0][0])
  2438. except:
  2439. pass
  2440. # Set netbox.uptodate = false (to make gdd update this device)
  2441. fields['uptodate'] = 'f'
  2442. # Update netbox
  2443. updateEntryFields(fields,'netbox','netboxid',selected)
  2444. # Update device (unless the serial was found in an already
  2445. # existing device)
  2446. if not deviceId:
  2447. if len(form['serial']) and \
  2448. (form['serial']!=oldBox.device.serial):
  2449. # Set new serial, if it has changed
  2450. fields = {'serial': form['serial']}
  2451. deviceId = str(oldBox.device.deviceid)
  2452. updateEntryFields(fields,'device','deviceid',deviceId)
  2453. # Remove old subcat and function entries
  2454. netboxId = oldBox.netboxid
  2455. deleteEntry([netboxId],'netboxcategory','netboxid')
  2456. deleteEntry([netboxId],'netboxinfo','netboxid')
  2457. # If subcatlist and function is given, insert them
  2458. if subcatlist:
  2459. for sc in subcatlist:
  2460. fields = {'netboxid': netboxId,
  2461. 'category': sc}
  2462. addEntryFields(fields,'netboxcategory')
  2463. if function:
  2464. fields = {'netboxid': netboxId,
  2465. 'key': '',
  2466. 'var': 'function',
  2467. 'val': function}
  2468. addEntryFields(fields,'netboxinfo')
  2469. action = 'list'
  2470. status.messages.append('Updated IP device ' + form['sysname'] + ' (' + \
  2471. form['ip'] + ')')
  2472. if not step == STEP_2:
  2473. # Unless this is the last step, set the nextStep
  2474. editboxHidden.addHidden(CNAME_STEP,nextStep)
  2475. return (status,action,templateform,selected)
  2476. class pageOrg(seeddbPage):
  2477. """ Describes editing of the org table. """
  2478. basePath = BASEPATH + 'org/'
  2479. table = nav.db.manage.Org
  2480. pageName = 'org'
  2481. tableName = 'org'
  2482. tableIdKey = 'orgid'
  2483. sequence = None
  2484. editMultipleAllowed = True
  2485. editIdAllowed = True
  2486. # Description format used by describe(id)
  2487. # Example: room 021 (descr) to jack 123, building, office
  2488. descriptionFormat = [('','orgid')]
  2489. # Unique fields (for errormessages from add/update)
  2490. unique = 'orgid'
  2491. # Nouns
  2492. singular = 'organisation'
  2493. plural = 'organisations'
  2494. # Delete dependencies
  2495. dependencies = [(manage.Org,
  2496. 'organisations',
  2497. 'parent',
  2498. '/report/org/?parent='),
  2499. (manage.Netbox,
  2500. 'boxes',
  2501. 'orgid',
  2502. '/report/netbox/?orgid=')]
  2503. pathAdd = EDITPATH + [('Organisation',basePath+'list'),('Add',False)]
  2504. pathEdit = EDITPATH + [('Organisation',basePath+'list'),('Edit',False)]
  2505. pathDelete = EDITPATH + [('Organisation',basePath+'list'),('Delete',False)]
  2506. pathList = EDITPATH + [('Organisation',False)]
  2507. class listDef(entryList):
  2508. """ Describes org list view """
  2509. def __init__(self,req,struct,sort,deleteWhere=None):
  2510. # Do general init
  2511. entryList.__init__(self,req,struct,sort,deleteWhere)
  2512. # Specific init
  2513. self.defaultSortBy = 1
  2514. # list of (heading text, show sortlink, compare function for sort)
  2515. self.headingDefinition = [('Select',False,None),
  2516. ('Organisation',True,None),
  2517. ('Parent',True,None),
  2518. ('Description',True,None),
  2519. ('Optional 1',True,None),
  2520. ('Optional 2',True,None),
  2521. ('Optional 3',True,None)]
  2522. self.cellDefinition = [(('orgid,orgid,parent,descr,opt1,opt2,opt3',
  2523. 'org',
  2524. None,
  2525. None,
  2526. 'orgid'),
  2527. [(None,None,entryListCell.CHECKBOX,None,None),
  2528. (1,'{p}edit/{id}',None,None,None),
  2529. (2,None,None,None,None),
  2530. (3,None,None,None,None),
  2531. (4,None,None,None,None),
  2532. (5,None,None,None,None),
  2533. (6,None,None,None,None)])]
  2534. class editbox(editbox):
  2535. """ Describes fields for adding and editing org entries.
  2536. The template uses this field information to draw the form. """
  2537. def __init__(self,page,req=None,editId=None,formData=None):
  2538. self.page = page.pageName
  2539. self.table = page.table
  2540. # Field definitions {field name: [input object, required]}
  2541. o = [('','No parent')]
  2542. for org in self.table.getAllIterator(orderBy='orgid'):
  2543. o.append((org.orgid,org.orgid + \
  2544. ' (' + str(org.descr) + ')'))
  2545. f = {'orgid': [inputText(maxlength=30),REQ_TRUE,'Organisation',
  2546. FIELD_STRING],
  2547. 'parent': [inputSelect(options=o),REQ_NONEMPTY,'Parent',
  2548. FIELD_STRING],
  2549. 'descr': [inputText(),REQ_FALSE,'Description',FIELD_STRING],
  2550. 'opt1': [inputText(),REQ_FALSE,'Optional 1',FIELD_STRING],
  2551. 'opt2': [inputText(),REQ_FALSE,'Optional 2',FIELD_STRING],
  2552. 'opt3': [inputText(),REQ_FALSE,'Optional 3',FIELD_STRING]}
  2553. self.fields = f
  2554. self.setControlNames()
  2555. if editId:
  2556. self.editId = editId
  2557. self.fill()
  2558. if formData:
  2559. self.formFill(formData)
  2560. class pagePatch(seeddbPage):
  2561. """ Describes editing of the patch table. """
  2562. basePath = BASEPATH + 'patch/'
  2563. table = nav.db.manage.Patch
  2564. pageName = 'patch'
  2565. tableName = 'patch'
  2566. tableIdKey = 'patchid'
  2567. sequence = 'patch_patchid_seq'
  2568. editMultipleAllowed = False
  2569. editIdAllowed = False
  2570. # Set action which makes browser jump to the first
  2571. # selectTree layoutbox. Makes editing easier on screens
  2572. # with low y resolution.
  2573. action = basePath + 'edit#top'
  2574. # Unique fields (for errormessages from add/update)
  2575. # Set to none since this page checks this in it's own
  2576. # add/update functions
  2577. unique = None
  2578. # Nouns
  2579. singular = 'patch'
  2580. plural = 'patches'
  2581. # Delete dependencies
  2582. dependencies = []
  2583. # Description format used by describe(id)
  2584. # Example: from netbox,module x,port y to jack z,room,building,location
  2585. descriptionFormat = [('from \'','cabling.jack'),
  2586. (', ','cabling.targetroom'),
  2587. (', ','cabling.building'),
  2588. (', ','cabling.room.location.locationid'),
  2589. ('\' to switch \'','swport.module.netbox.sysname'),
  2590. (', module ','swport.module.module'),
  2591. (', port ','swport.port'),
  2592. ('\'',None)]
  2593. pathAdd = EDITPATH + [('Patch',basePath+'list'),('Add',False)]
  2594. pathEdit = EDITPATH + [('Patch',basePath+'list'),('Edit',False)]
  2595. pathDelete = EDITPATH + [('Patch',basePath+'list'),('Delete',False)]
  2596. pathList = EDITPATH + [('Patch',False)]
  2597. class listDef(entryList):
  2598. """ Describes patch list view """
  2599. def __init__(self,req,struct,sort,deleteWhere=None):
  2600. # Do general init
  2601. entryList.__init__(self,req,struct,sort,deleteWhere)
  2602. # Specific init
  2603. self.defaultSortBy = 1
  2604. # List filters
  2605. self.filters = [['Show patches in room ',
  2606. ('','Select a room'),
  2607. ('roomid,descr',
  2608. 'room',
  2609. None,
  2610. '((SELECT count(*) FROM patch,cabling WHERE ' +\
  2611. 'patch.cablingid=cabling.cablingid AND ' +\
  2612. 'cabling.roomid=room.roomid) > 0)',
  2613. 'room.roomid'),
  2614. '{id}',
  2615. '{id} ({descr})',
  2616. 'flt_room',
  2617. 'cabling.roomid',
  2618. nav.db.manage.Patch,
  2619. 'cabling.room.roomid']]
  2620. # list of (heading text, show sortlink, compare function for sort)
  2621. self.headingDefinition = [('Select',False,None),
  2622. ('Switch',True,None),
  2623. ('Module',True,None),
  2624. ('Port',True,None),
  2625. ('Room',True,None),
  2626. ('Jack',True,None),
  2627. ('Split',True,None)]
  2628. self.cellDefinition = [(('patch.patchid,netbox.sysname,' +\
  2629. 'module.module,swport.port,' +\
  2630. 'room.roomid,cabling.jack,patch.split',
  2631. 'patch,cabling,room,netbox,swport,module',
  2632. None,
  2633. 'patch.cablingid=cabling.cablingid ' +\
  2634. 'AND cabling.roomid=room.roomid AND ' +\
  2635. 'patch.swportid=swport.swportid AND ' +\
  2636. 'swport.moduleid=module.moduleid AND ' +\
  2637. 'module.netboxid=netbox.netboxid',
  2638. 'room.locationid,room.roomid,' +\
  2639. 'netbox.sysname,module.module,' +\
  2640. 'swport.port'),
  2641. [(None,None,entryListCell.CHECKBOX,None,None),
  2642. (1,'{p}edit/{id}',None,None,None),
  2643. (2,None,None,None,None),
  2644. (3,None,None,None,None),
  2645. (4,None,None,None,None),
  2646. (5,None,None,None,None),
  2647. (6,None,None,None,None)])]
  2648. class editbox(editbox):
  2649. """ Describes fields for adding and editing patch entries.
  2650. The template uses this field information to display the form.
  2651. This box uses the selectTree classes and updates itself
  2652. instead of using seeddbPage.formFill(). """
  2653. def __init__(self,page,req=None,editId=None,formData=None):
  2654. self.page = page.pageName
  2655. self.table = page.table
  2656. self.hiddenFields = {}
  2657. # Set the name of this boxname to reflect that we are
  2658. # updating an entry
  2659. if editId:
  2660. self.boxName = UPDATE_ENTRY
  2661. self.boxId = editId
  2662. self.addHidden(UPDATE_ENTRY,editId)
  2663. selectedSwport = []
  2664. selectedModule = []
  2665. selectedSwitch = []
  2666. selectedJack = []
  2667. selectedRoom = []
  2668. selectedLocation = []
  2669. split = None
  2670. addRoom = []
  2671. addJack = []
  2672. addSwport = []
  2673. if editId:
  2674. # Preserve the selected id
  2675. self.addHidden(selectList.cnameChk,editId)
  2676. patch = self.table(editId)
  2677. selectedSwport = [patch.swport.swportid]
  2678. selectedModule = [patch.swport.module.moduleid]
  2679. selectedSwitch = [patch.swport.module.netbox.netboxid]
  2680. selectedJack = [patch.cabling.cablingid]
  2681. selectedRoom = [patch.cabling.room.roomid]
  2682. selectedLocation = [patch.cabling.room.location.locationid]
  2683. split = patch.split
  2684. # Entries which must be added manually since the sql
  2685. # excludes them (not to be shown while adding, but must
  2686. # be shown while editing since they are selected)
  2687. addRoom = [(patch.cabling.room.location.locationid,
  2688. patch.cabling.room.roomid,
  2689. patch.cabling.room.roomid + ' (' +\
  2690. patch.cabling.room.descr + ')',
  2691. True)]
  2692. addJack = [(patch.cabling.room.roomid,
  2693. str(patch.cabling.cablingid),
  2694. patch.cabling.jack,
  2695. True)]
  2696. addSwport = [(str(patch.swport.module.moduleid),
  2697. str(patch.swport.swportid),
  2698. str(patch.swport.port),
  2699. True)]
  2700. self.help = 'Add or update a patch by selecting a jack and a ' +\
  2701. 'switchport. Optionally select a split. Only rooms '+\
  2702. 'with at least one switch and at least one available '+\
  2703. 'jack are listed. Available jacks have either '+\
  2704. 'no patch or at most one splitted patch connected.'
  2705. if req:
  2706. select1 = simpleSelect('Location',
  2707. 'cn_loc',
  2708. ('locationid,descr',
  2709. 'location',
  2710. None,
  2711. None,
  2712. 'locationid'),
  2713. selectedLocation,
  2714. optionFormat='$1 ($2)',
  2715. selectMultiple=False,
  2716. multipleHeight=8)
  2717. # SQL (where) for selecting rooms
  2718. # Only selects rooms which have one or more boxes of
  2719. # category EDGE,SW or GSW and where there are "available"
  2720. # jacks. Available means a jack which isn't already in
  2721. # use by a patch, or a jack which is use by one patch
  2722. # but is splitted.
  2723. roomSQL = """((SELECT count(*) FROM cabling WHERE cabling.roomid=room.roomid AND (((SELECT count(*) FROM patch WHERE patch.cablingid=cabling.cablingid AND patch.split='no') = 0) AND ((SELECT count(*) FROM patch WHERE patch.cablingid=cabling.cablingid AND patch.split!='no')) < 2)) > 0) AND ((SELECT count(*) FROM netbox WHERE roomid=room.roomid AND (netbox.catid='SW' OR netbox.catid='GSW' OR netbox.catid='EDGE')) > 0)"""
  2724. select2 = updateSelect(select1,
  2725. 'locationid',
  2726. 'Room',
  2727. 'cn_room',
  2728. ('roomid,descr',
  2729. 'room',
  2730. None,
  2731. roomSQL,
  2732. 'roomid'),
  2733. selectedRoom,
  2734. addRoom,
  2735. optionFormat='$1 ($2)',
  2736. selectMultiple=False,
  2737. multipleHeight=8)
  2738. # SQL (where) for selecting jacks
  2739. # Selects "available" jacks. Available means a jack which isn't
  2740. # already in use by a patch, or a jack which is use by one
  2741. # patch but is splitted.
  2742. jackSQL = """(((SELECT count(*) FROM patch WHERE patch.cablingid=cabling.cablingid AND patch.split='no') = 0) AND ((SELECT count(*) FROM patch WHERE patch.cablingid=cabling.cablingid AND patch.split!='no') < 2))"""
  2743. select3 = updateSelect(select2,
  2744. 'roomid',
  2745. 'Jack',
  2746. 'cn_jack',
  2747. ('cablingid,jack',
  2748. 'cabling',
  2749. None,
  2750. jackSQL,
  2751. 'jack'),
  2752. selectedJack,
  2753. addJack,
  2754. optgroupFormat='Room $1',
  2755. setOnChange=True,
  2756. selectMultiple=False,
  2757. multipleHeight=8)
  2758. whereSwitch = "(catid='EDGE' or catid='SW' or catid='GSW')"
  2759. select4 = updateSelect(select2,
  2760. 'roomid',
  2761. 'Switch',
  2762. 'cn_switch',
  2763. ('netboxid,sysname',
  2764. 'netbox',
  2765. None,
  2766. whereSwitch,
  2767. 'sysname'),
  2768. selectedSwitch,
  2769. optgroupFormat='Room $1',
  2770. selectMultiple=False,
  2771. multipleHeight=8)
  2772. select5 = updateSelect(select4,
  2773. 'netboxid',
  2774. 'Module',
  2775. 'cn_module',
  2776. ('moduleid,module',
  2777. 'module',
  2778. None,
  2779. None,
  2780. 'module'),
  2781. selectedModule,
  2782. optgroupFormat='$2',
  2783. selectMultiple=False,
  2784. multipleHeight=8)
  2785. # Don't show swports that are already in use
  2786. swportSQL = "((SELECT count(*) FROM patch WHERE patch.swportid=swport.swportid) < 1)"
  2787. select6 = updateSelect(select5,
  2788. 'moduleid',
  2789. 'Port',
  2790. 'cn_swport',
  2791. ('swportid,port',
  2792. 'swport',
  2793. None,
  2794. swportSQL,
  2795. 'port'),
  2796. selectedSwport,
  2797. addSwport,
  2798. optgroupFormat='Module $2',
  2799. setOnChange=False,
  2800. selectMultiple=False,
  2801. multipleHeight=8)
  2802. st = selectTree()
  2803. st.addSelect(select1)
  2804. st.addSelect(select2)
  2805. st.addSelect(select3)
  2806. st.addSelect(select4)
  2807. st.addSelect(select5)
  2808. st.addSelect(select6)
  2809. st.update(req.form)
  2810. lb = selectTreeLayoutBox(htmlId='top')
  2811. lb.addSelect(select1)
  2812. lb.addSelect(select2)
  2813. lb.addSelect(select3)
  2814. lb2 = selectTreeLayoutBox()
  2815. lb2.addSelect(select4)
  2816. lb2.addSelect(select5)
  2817. lb2.addSelect(select6)
  2818. # Check if the selected jack is already used in a patch
  2819. # (ie. a split). In that case, set the split select to
  2820. # the same value in this form
  2821. if req.form.has_key('cn_jack'):
  2822. if len(req.form['cn_jack']):
  2823. where = "cablingid='" + req.form['cn_jack'] + "'"
  2824. patch = nav.db.manage.Patch.getAll(where)
  2825. if patch:
  2826. # Already exists a patch with this jack, it must
  2827. # be splitted, select the same split in this form
  2828. patch = patch[0]
  2829. split = SPLIT_OPPOSITE[patch.split]
  2830. # Remember split from form (overrides split from other patch)
  2831. if req.form.has_key('split'):
  2832. if req.form['split'] != 'no':
  2833. split = req.form['split']
  2834. else:
  2835. lb = None
  2836. lb2 = None
  2837. # Field definitions {field name: [input object, required]}
  2838. f = {'box1': [inputTreeSelect(treeselect=lb),
  2839. REQ_FALSE,'Room',FIELD_STRING],
  2840. 'box2': [inputTreeSelect(treeselect=lb2),
  2841. REQ_FALSE,'Switch port',FIELD_STRING],
  2842. 'split': [inputSelect(options=SPLIT_LIST),REQ_FALSE,
  2843. 'Split',FIELD_STRING]}
  2844. if split:
  2845. f['split'][0].value = split
  2846. self.fields = f
  2847. self.setControlNames()
  2848. def add(self,req,templateForm,action):
  2849. """ Adds patch entries. Overrides the default add function. """
  2850. error = None
  2851. status = seeddbStatus()
  2852. split = None
  2853. cablingid = None
  2854. swportid = None
  2855. if req.form.has_key('split'):
  2856. split = req.form['split']
  2857. else:
  2858. error = "Missing required field 'Split'"
  2859. if req.form.has_key('cn_jack'):
  2860. cablingid = req.form['cn_jack']
  2861. else:
  2862. error = "Missing required field 'Jack'"
  2863. if req.form.has_key('cn_swport'):
  2864. swportid = req.form['cn_swport']
  2865. else:
  2866. error = "Missing required field 'Port'"
  2867. if not error:
  2868. # Check if the selected jack already belongs to a patch
  2869. where = "cablingid='" + cablingid + "'"
  2870. otherPatch = nav.db.manage.Patch.getAll(where)
  2871. if otherPatch:
  2872. # Already exists a patch with this jack, it must
  2873. # be splitted, if split is changed then do something
  2874. otherPatch = otherPatch[0]
  2875. otherSplit = otherPatch.split
  2876. if SPLIT_OPPOSITE[split] != otherSplit:
  2877. # Splits are different, either update split on the
  2878. # other entry, or delete it if this split='no'
  2879. otherPatchId = str(otherPatch.patchid)
  2880. # SPLIT_LIST[0][0] is default entry id
  2881. if split == SPLIT_LIST[0][0]:
  2882. # Delete other entry
  2883. deleteEntry([otherPatchId],'patch','patchid')
  2884. else:
  2885. # Update other entry
  2886. fields = {'split': SPLIT_OPPOSITE[split]}
  2887. updateEntryFields(fields,self.tableName,
  2888. self.tableIdKey,otherPatchId)
  2889. fields = {'cablingid': cablingid,
  2890. 'swportid': swportid,
  2891. 'split': split}
  2892. try:
  2893. patchId = addEntryFields(fields,self.tableName,
  2894. (self.tableIdKey,self.sequence))
  2895. action = 'list'
  2896. except psycopg2.IntegrityError,e:
  2897. error = 'There already exists a patch from this jack ' +\
  2898. 'to that port'
  2899. if error:
  2900. status.errors.append(error)
  2901. else:
  2902. message = 'Added patch: ' + self.describe(patchId)
  2903. status.messages.append(message)
  2904. action = 'list'
  2905. return (status,action,templateForm,patchId)
  2906. def update(self,req,outputForm,selected):
  2907. """ Updates patch entries. Overrides the default update function. """
  2908. status = seeddbStatus()
  2909. error = None
  2910. action = 'edit'
  2911. split = None
  2912. cablingid = None
  2913. swportid = None
  2914. if req.form.has_key('split'):
  2915. split = req.form['split']
  2916. else:
  2917. error = "Missing required field 'Split'"
  2918. if req.form.has_key('cn_jack'):
  2919. cablingid = req.form['cn_jack']
  2920. else:
  2921. error = "Missing required field 'Jack'"
  2922. if req.form.has_key('cn_swport'):
  2923. swportid = req.form['cn_swport']
  2924. else:
  2925. error = "Missing required field 'Port'"
  2926. if not error:
  2927. # Check if the selected jack belongs to another patch
  2928. where = "cablingid='" + cablingid + "' AND NOT patchid='" +\
  2929. selected[0] + "'"
  2930. otherPatch = nav.db.manage.Patch.getAll(where)
  2931. if otherPatch:
  2932. # Already exists a patch with this jack
  2933. otherPatch = otherPatch[0]
  2934. #otherSplit = otherPatch.split
  2935. # Update other split
  2936. otherPatchId = str(otherPatch.patchid)
  2937. # SPLIT_LIST[0][0] is default entry id
  2938. if split == SPLIT_LIST[0][0]:
  2939. # Delete other entry
  2940. deleteEntry([otherPatchId],'patch','patchid')
  2941. else:
  2942. # Update other entry
  2943. fields = {'split': SPLIT_OPPOSITE[split]}
  2944. updateEntryFields(fields,self.tableName,self.tableIdKey,
  2945. otherPatchId)
  2946. fields = {'cablingid': cablingid,
  2947. 'swportid': swportid,
  2948. 'split': split}
  2949. try:
  2950. updateEntryFields(fields,self.tableName,self.tableIdKey,
  2951. selected[0])
  2952. action = 'list'
  2953. except psycopg2.IntegrityError,e:
  2954. error = 'There already exists a patch from this swport ' +\
  2955. 'to that jack'
  2956. action = 'edit'
  2957. if error:
  2958. status.errors.append(error)
  2959. else:
  2960. message = 'Updated patch: ' + self.describe(selected[0])
  2961. status.messages.append(message)
  2962. action = 'list'
  2963. return (status,action,outputForm,selected)
  2964. class pagePrefix(seeddbPage):
  2965. """ Describes editing of the prefix table for nettypes of 'reserved' or
  2966. 'scope'. """
  2967. basePath = BASEPATH + 'prefix/'
  2968. table = nav.db.manage.Prefix
  2969. pageName = 'prefix'
  2970. tableName = 'prefix'
  2971. tableIdKey = 'prefixid'
  2972. sequence = 'prefix_prefixid_seq'
  2973. editMultipleAllowed = False
  2974. editIdAllowed = False
  2975. # Description format used by describe(id)
  2976. descriptionFormat = [('','netaddr'),
  2977. (', ','vlan.nettype')]
  2978. # Unique fields (for errormessages from add/update)
  2979. unique = 'netaddr'
  2980. # Nouns
  2981. singular = 'prefix'
  2982. plural = 'prefixes'
  2983. # Delete dependencies
  2984. dependencies = []
  2985. pathAdd = EDITPATH + [('Prefix',basePath+'list'),('Add',False)]
  2986. pathEdit = EDITPATH + [('Prefix',basePath+'list'),('Edit',False)]
  2987. pathDelete = EDITPATH + [('Prefix',basePath+'list'),('Delete',False)]
  2988. pathList = EDITPATH + [('Prefix',False)]
  2989. class listDef(entryList):
  2990. """ Describes prefix list view """
  2991. def __init__(self,req,struct,sort,deleteWhere=None):
  2992. # Do general init
  2993. entryList.__init__(self,req,struct,sort,deleteWhere)
  2994. # Specific init
  2995. self.defaultSortBy = 1
  2996. # list of (heading text, show sortlink, compare function for sort)
  2997. self.headingDefinition = [('Select',False,None),
  2998. ('Prefix/mask',True,None),
  2999. ('Nettype',True,None),
  3000. ('Org',True,None),
  3001. ('Netident',True,None),
  3002. ('Usage',True,None),
  3003. ('Description',True,None),
  3004. ('Vlan',True,None)]
  3005. where = "(vlan.nettype in (SELECT nettypeid " + \
  3006. " FROM nettype " + \
  3007. " WHERE edit='t'))"
  3008. self.cellDefinition = [(('prefix.prefixid,netaddr,vlan.nettype,' +\
  3009. 'vlan.orgid,vlan.netident,vlan.usageid,' +\
  3010. 'vlan.description,vlan.vlan',
  3011. 'prefix,vlan',
  3012. None,
  3013. 'prefix.vlanid=vlan.vlanid AND ' + where,
  3014. 'prefix.netaddr,nettype'),
  3015. [(None,None,entryListCell.CHECKBOX,None,None),
  3016. (1,'{p}edit/{id}',None,None,None),
  3017. (2,None,None,None,None),
  3018. (3,None,None,None,None),
  3019. (4,None,None,None,None),
  3020. (5,None,None,None,None),
  3021. (6,None,None,None,None),
  3022. (7,None,None,None,None)])]
  3023. class editbox(editbox):
  3024. """ Describes fields for adding and editing patch entries.
  3025. The template uses this field information to display the form. """
  3026. # must get most of the fields from the vlan table
  3027. additionalSQL = ' AND prefix.vlanid=vlan.vlanid'
  3028. def __init__(self,page,req=None,editId=None,formData=None):
  3029. self.page = page.pageName
  3030. self.table = page.table
  3031. nettypes = [('reserved','reserved'),
  3032. ('scope','scope')]
  3033. orgs = [('','No organisation')]
  3034. for o in nav.db.manage.Org.getAllIterator(orderBy='orgid'):
  3035. orgs.append((o.orgid,o.orgid + ' (' + str(o.descr) + ')'))
  3036. usageids = [('','No usage')]
  3037. for usage in manage.Usage.getAllIterator(orderBy='usageid'):
  3038. usageids.append((usage.usageid,usage.usageid + ' (' + \
  3039. usage.descr + ')'))
  3040. # Field definitions {field name: [input object, required]}
  3041. f = {'netaddr': [inputText(),REQ_TRUE,'Prefix/mask (cidr)',
  3042. FIELD_STRING],
  3043. 'vlan.nettype': [inputSelect(options=nettypes),REQ_TRUE,
  3044. 'Network type',FIELD_STRING],
  3045. 'vlan.orgid': [inputSelect(options=orgs),REQ_NONEMPTY,
  3046. 'Organisation',FIELD_STRING],
  3047. 'vlan.netident': [inputText(),REQ_FALSE,'Netident',
  3048. FIELD_STRING],
  3049. 'vlan.description': [inputText(),REQ_FALSE,'Description',
  3050. FIELD_STRING],
  3051. 'vlan.vlan': [inputText(size=5),REQ_NONEMPTY,'Vlan',
  3052. FIELD_INTEGER],
  3053. 'vlan.usageid': [inputSelect(options=usageids),REQ_NONEMPTY,
  3054. 'Usage',FIELD_STRING]}
  3055. self.fields = f
  3056. self.setControlNames()
  3057. if editId:
  3058. self.editId = editId
  3059. self.fill()
  3060. if formData:
  3061. self.formFill(formData)
  3062. def add(self,req,templateForm,action):
  3063. """ Adds prefix entries. Overrides the default add function. """
  3064. error = None
  3065. status = seeddbStatus()
  3066. data = {'nettype': req.form['vlan.nettype'],
  3067. 'description': req.form['vlan.description'],
  3068. 'netaddr': req.form['netaddr'],
  3069. 'netident': req.form['vlan.netident'],
  3070. 'vlan': req.form['vlan.vlan']}
  3071. if len(req.form['vlan.orgid']):
  3072. data['orgid'] = req.form['vlan.orgid']
  3073. if len(req.form['vlan.usageid']):
  3074. data['usageid'] = req.form['vlan.usageid']
  3075. error = self.insertPrefix(data)
  3076. if not error:
  3077. action = 'list'
  3078. status.messages.append('Added prefix')
  3079. else:
  3080. action = 'add'
  3081. status.errors.append(error)
  3082. return (status,action,templateForm,None)
  3083. def insertPrefix(self,data):
  3084. """ Inserts prefixes into the database. Used by pagePrefix.add() """
  3085. error = None
  3086. # Add new vlan
  3087. fields = {'nettype': data['nettype']}
  3088. if data.has_key('orgid'):
  3089. if len(data['orgid']):
  3090. fields['orgid'] = data['orgid']
  3091. if data.has_key('usageid'):
  3092. if len(data['usageid']):
  3093. fields['usageid'] = data['usageid']
  3094. if len(data['description']):
  3095. fields['description'] = data['description']
  3096. if len(data['vlan']):
  3097. fields['vlan'] = data['vlan']
  3098. if len(data['netident']):
  3099. fields['netident'] = data['netident']
  3100. vlanid = addEntryFields(fields,
  3101. 'vlan',
  3102. ('vlanid','vlan_vlanid_seq'))
  3103. # Add new prefix
  3104. fields = {'netaddr': data['netaddr'],
  3105. 'vlanid': vlanid}
  3106. try:
  3107. addEntryFields(fields,'prefix')
  3108. except psycopg2.ProgrammingError:
  3109. # Invalid cidr
  3110. error = 'Invalid CIDR'
  3111. # Remove vlan entry
  3112. deleteEntry([vlanid],'vlan','vlanid')
  3113. except psycopg2.IntegrityError:
  3114. # Already existing cidr
  3115. error = 'Prefix already exists in database'
  3116. deleteEntry([vlanid],'vlan','vlanid')
  3117. return error
  3118. def update(self,req,templateform,selected):
  3119. """ Updates prefix entries. Overrides the default update function. """
  3120. error = None
  3121. status = seeddbStatus()
  3122. formdata = {}
  3123. idlist = []
  3124. if type(req.form[UPDATE_ENTRY]) is list:
  3125. # editing several entries
  3126. for i in range(0,len(req.form[UPDATE_ENTRY])):
  3127. entry = {}
  3128. editid = req.form[UPDATE_ENTRY][i]
  3129. idlist.append(editid)
  3130. entry['netaddr'] = req.form['netaddr'][i]
  3131. entry['description'] = req.form['vlan.description'][i]
  3132. entry['netident'] = req.form['vlan.netident'][i]
  3133. entry['orgid'] = req.form['vlan.orgid'][i]
  3134. entry['usageid'] = req.form['vlan.usageid'][i]
  3135. entry['nettype'] = req.form['vlan.nettype'][i]
  3136. entry['vlan'] = req.form['vlan.vlan'][i]
  3137. formdata[editid] = entry
  3138. else:
  3139. # editing just one entry
  3140. entry = {}
  3141. editid = req.form[UPDATE_ENTRY]
  3142. idlist = [editid]
  3143. entry['netaddr'] = req.form['netaddr']
  3144. entry['description'] = req.form['vlan.description']
  3145. entry['netident'] = req.form['vlan.netident']
  3146. entry['orgid'] = req.form['vlan.orgid']
  3147. entry['usageid'] = req.form['vlan.usageid']
  3148. entry['nettype'] = req.form['vlan.nettype']
  3149. entry['vlan'] = req.form['vlan.vlan']
  3150. formdata[editid] = entry
  3151. for updateid,data in formdata.items():
  3152. vlanfields = {'description': data['description'],
  3153. 'netident': data['netident'],
  3154. 'nettype': data['nettype']}
  3155. if len(data['orgid']):
  3156. vlanfields['orgid'] = data['orgid']
  3157. else:
  3158. vlanfields['orgid'] = None
  3159. if len(data['usageid']):
  3160. vlanfields['usageid'] = data['usageid']
  3161. else:
  3162. vlanfields['usageid'] = None
  3163. if len(data['vlan']):
  3164. vlanfields['vlan'] = data['vlan']
  3165. else:
  3166. vlanfields['vlan'] = None
  3167. prefixfields = {'netaddr': data['netaddr']}
  3168. vlanid = manage.Prefix(updateid).vlan.vlanid
  3169. updateEntryFields(vlanfields,'vlan','vlanid',str(vlanid))
  3170. updateEntryFields(prefixfields,'prefix','prefixid',updateid)
  3171. selected = idlist
  3172. action = 'list'
  3173. if not error:
  3174. action = 'list'
  3175. status.messages.append('Updated prefix: ' + self.describe(updateid))
  3176. else:
  3177. action = 'edit'
  3178. status.errors.append(error)
  3179. return (status,action,templateform,selected)
  3180. class pageProduct(seeddbPage):
  3181. """ Describes editing of the product table. """
  3182. basePath = BASEPATH + 'product/'
  3183. table = nav.db.manage.Product
  3184. pageName = 'product'
  3185. tableName = 'product'
  3186. tableIdKey = 'productid'
  3187. sequence = 'product_productid_seq'
  3188. editMultipleAllowed = True
  3189. editIdAllowed = False
  3190. # Description format used by describe(id)
  3191. descriptionFormat = [('','vendor.vendorid'),
  3192. (', ','productno')]
  3193. # Unique fields (for errormessages from add/update)
  3194. unique = ['vendorid']
  3195. # Nouns
  3196. singular = 'product'
  3197. plural = 'products'
  3198. # Delete dependencies
  3199. dependencies = []
  3200. pathAdd = EDITPATH + [('Product',basePath+'list'),('Add',False)]
  3201. pathEdit = EDITPATH + [('Product',basePath+'list'),('Edit',False)]
  3202. pathDelete = EDITPATH + [('Product',basePath+'list'),('Delete',False)]
  3203. pathList = EDITPATH + [('Product',False)]
  3204. class listDef(entryList):
  3205. """ Describes product list view """
  3206. def __init__(self,req,struct,sort,deleteWhere=None):
  3207. # Do general init
  3208. entryList.__init__(self,req,struct,sort,deleteWhere)
  3209. # Specific init
  3210. self.defaultSortBy = 1
  3211. # list of (heading text, show sortlink, compare function for sort)
  3212. self.headingDefinition = [('Select',False,None),
  3213. ('Vendor',True,None),
  3214. ('Productno',True,None),
  3215. ('Description',True,None)]
  3216. self.cellDefinition = [(('productid,vendorid,productno,descr',
  3217. 'product',
  3218. None,
  3219. None,
  3220. 'vendorid,productno'),
  3221. [(None,None,entryListCell.CHECKBOX,None,None),
  3222. (1,'{p}edit/{id}',None,None,None),
  3223. (2,None,None,None,None),
  3224. (3,None,None,None,None)])]
  3225. class editbox(editbox):
  3226. """ Describes fields for adding and editing product entries.
  3227. The template uses this field information to display the form. """
  3228. def __init__(self,page,req=None,editId=None,formData=None):
  3229. self.page = page.pageName
  3230. self.table = page.table
  3231. # Field definitions {field name: [input object, required]}
  3232. f = {'vendorid': [inputSelect(table=seeddbTables.seeddbVendor),
  3233. REQ_TRUE,'Vendor',FIELD_STRING],
  3234. 'productno': [inputText(),REQ_TRUE,'Productno',FIELD_STRING],
  3235. 'descr': [inputText(),REQ_FALSE,'Description',FIELD_STRING]}
  3236. self.fields = f
  3237. self.setControlNames()
  3238. if editId:
  3239. self.editId = editId
  3240. self.fill()
  3241. if formData:
  3242. self.formFill(formData)
  3243. class pageRoom(seeddbPage):
  3244. """ Describes editing of the room table. """
  3245. basePath = BASEPATH + 'room/'
  3246. table = nav.db.manage.Room
  3247. pageName = 'room'
  3248. tableName = 'room'
  3249. tableIdKey = 'roomid'
  3250. sequence = None
  3251. editMultipleAllowed = True
  3252. editIdAllowed = True
  3253. # Description format used by describe(id)
  3254. # Example: room 021 (descr) to jack 123, building, office
  3255. # BUG: roomid.roomid should only have to be roomid
  3256. # can't find any error in describe()
  3257. descriptionFormat = [('','roomid.roomid')]
  3258. # Unique fields (for errormessages from add/update)
  3259. unique = 'roomid'
  3260. # Nouns
  3261. singular = 'room'
  3262. plural = 'rooms'
  3263. # Delete dependencies
  3264. dependencies = [(nav.db.manage.Netbox,
  3265. 'boxes',
  3266. 'roomid',
  3267. '/report/netbox/?roomid=')]
  3268. pathAdd = EDITPATH + [('Room',basePath+'list'),('Add',False)]
  3269. pathEdit = EDITPATH + [('Room',basePath+'list'),('Edit',False)]
  3270. pathDelete = EDITPATH + [('Room',basePath+'list'),('Delete',False)]
  3271. pathList = EDITPATH + [('Room',False)]
  3272. class listDef(entryList):
  3273. """ Describes room list view """
  3274. def __init__(self,req,struct,sort,deleteWhere=None):
  3275. # Do general init
  3276. entryList.__init__(self,req,struct,sort,deleteWhere)
  3277. # Specific init
  3278. self.defaultSortBy = 1
  3279. # list of (heading text, show sortlink, compare function for sort)
  3280. self.headingDefinition = [('Select',False,None),
  3281. ('Room Id',True,None),
  3282. ('Location',True,None),
  3283. ('Description',True,None),
  3284. ('Optional 1',True,None),
  3285. ('Optional 2',True,None),
  3286. ('Optional 3',True,None),
  3287. ('Optional 4',True,None)]
  3288. self.cellDefinition = [(('roomid,roomid,locationid,descr,' +\
  3289. 'opt1,opt2,opt3,opt4',
  3290. 'room',
  3291. None,
  3292. None,
  3293. 'roomid,locationid'),
  3294. [(None,None,entryListCell.CHECKBOX,None,None),
  3295. (1,'{p}edit/{id}',None,None,None),
  3296. (2,None,None,None,None),
  3297. (3,None,None,None,None),
  3298. (4,None,None,None,None),
  3299. (5,None,None,None,None),
  3300. (6,None,None,None,None),
  3301. (7,None,None,None,None)])]
  3302. class editbox(editbox):
  3303. """ Describes fields for adding and editing room entries.
  3304. The template uses this field information to display the form. """
  3305. def __init__(self,page,req=None,editId=None,formData=None):
  3306. self.page = page.pageName
  3307. self.table = page.table
  3308. self.hiddenFields = {}
  3309. disabled = False
  3310. if editId and not pageRoom.editIdAllowed:
  3311. disabled = True
  3312. locations = [('','Select a location')]
  3313. for l in nav.db.manage.Location.getAllIterator(
  3314. orderBy='locationid'):
  3315. locations.append((l.locationid,l.locationid + ' (' +
  3316. l.descr + ')'))
  3317. f = {'roomid': [inputText(disabled=disabled,maxlength=30),
  3318. REQ_TRUE,'Room Id',FIELD_STRING],
  3319. 'locationid': [inputSelect(options=locations),
  3320. REQ_TRUE,'Location',FIELD_STRING],
  3321. 'descr': [inputText(),REQ_FALSE,'Description',FIELD_STRING],
  3322. 'opt1': [inputText(),REQ_FALSE,'Optional 1',FIELD_STRING],
  3323. 'opt2': [inputText(),REQ_FALSE,'Optional 2',FIELD_STRING],
  3324. 'opt3': [inputText(),REQ_FALSE,'Optional 3',FIELD_STRING],
  3325. 'opt4': [inputText(),REQ_FALSE,'Optional 4',FIELD_STRING]}
  3326. self.fields = f
  3327. self.setControlNames()
  3328. if editId:
  3329. self.editId = editId
  3330. self.fill()
  3331. if formData:
  3332. self.formFill(formData)
  3333. if disabled:
  3334. self.addDisabled()
  3335. class pageService(seeddbPage):
  3336. """ Describes editing of the service table. """
  3337. basePath = BASEPATH + 'service/'
  3338. table = nav.db.manage.Service
  3339. pageName = 'service'
  3340. tableName = 'service'
  3341. tableIdKey = 'serviceid'
  3342. sequence = 'service_serviceid_seq'
  3343. editMultipleAllowed = False
  3344. editIdAllowed = False
  3345. # Description format used by describe(id)
  3346. # Example: room 021 (descr) to jack 123, building, office
  3347. descriptionFormat = [('','handler'),
  3348. (' on ','netbox.sysname')]
  3349. # Unique fields (for errormessages from add/update)
  3350. unique = ''
  3351. # Nouns
  3352. singular = 'service'
  3353. plural = 'services'
  3354. # Delete dependencies
  3355. dependencies = []
  3356. pathAdd = EDITPATH + [('Service',basePath+'list'),('Add',False)]
  3357. pathEdit = EDITPATH + [('Service',basePath+'list'),('Edit',False)]
  3358. pathDelete = EDITPATH + [('Service',basePath+'list'),('Delete',False)]
  3359. pathList = EDITPATH + [('Service',False)]
  3360. class listDef(entryList):
  3361. """ Describes service list view """
  3362. def __init__(self,req,struct,sort,deleteWhere=None):
  3363. # Do general init
  3364. entryList.__init__(self,req,struct,sort,deleteWhere)
  3365. # Specific init
  3366. self.defaultSortBy = 1
  3367. # list of (heading text, show sortlink, compare function for sort)
  3368. self.headingDefinition = [('Select',False,None),
  3369. ('Server',True,None),
  3370. ('Handler',True,None),
  3371. ('Version',True,None)]
  3372. self.cellDefinition = [(('serviceid,netbox.sysname,handler,version',
  3373. 'service,netbox',
  3374. None,
  3375. 'service.netboxid=netbox.netboxid',
  3376. 'netbox.sysname,handler'),
  3377. [(None,None,entryListCell.CHECKBOX,None,None),
  3378. (1,'{p}edit/{id}',None,None,None),
  3379. (2,None,None,None,None),
  3380. (3,None,None,None,None)])]
  3381. class editbox(editbox):
  3382. """ Describes fields for adding and editing service entries.
  3383. The template uses this field information to display the form. """
  3384. def __init__(self,page,req=None,editId=None,formData=None,
  3385. disabled=False):
  3386. self.page = page.pageName
  3387. self.table = page.table
  3388. self.hiddenFields = {}
  3389. self.editId = editId
  3390. self.help = ''
  3391. form = {}
  3392. if req:
  3393. form = req.form
  3394. propertyListFilled = []
  3395. handlerEntries = []
  3396. presentHandlers = []
  3397. if editId:
  3398. # Make editbox for editing properties (not for adding services)
  3399. self.help = 'Edit service properties for services on this ' +\
  3400. 'IP device'
  3401. # Set the name of this boxname to reflect that we are
  3402. # updating an entry
  3403. self.boxName = UPDATE_ENTRY
  3404. self.boxId = editId
  3405. # editId is serviceid, get netboxid
  3406. boxid = str(nav.db.manage.Service(editId).netbox.netboxid)
  3407. sql = "SELECT serviceid FROM service WHERE netboxid='" +\
  3408. boxid + "' ORDER BY handler"
  3409. result = executeSQLreturn(sql)
  3410. serviceIdList = []
  3411. if result:
  3412. for row in result:
  3413. serviceIdList.append(str(row[0]))
  3414. for serviceId in serviceIdList:
  3415. handler = nav.db.manage.Service(serviceId).handler
  3416. presentHandlers.append(handler)
  3417. handlerId = handler + '_' + serviceId
  3418. handlerName = handler
  3419. if presentHandlers.count(handler) > 0:
  3420. handlerName = handler + ' (' +\
  3421. str(presentHandlers.count(handler)) + ')'
  3422. handlerEntries.append((handlerId,handlerName,True))
  3423. sql = "SELECT * FROM serviceproperty WHERE " + \
  3424. "serviceid='" + serviceId + "'"
  3425. result = executeSQLreturn(sql)
  3426. propertyValues = {}
  3427. for entry in result:
  3428. property = entry[1]
  3429. value = entry[2]
  3430. propertyValues[property] = value
  3431. prop = self.makePropertyInput(handler,propertyValues,
  3432. serviceId=serviceId)
  3433. if prop:
  3434. propertyListFilled.append(prop)
  3435. # Preselected values
  3436. catid = nav.db.manage.Netbox(boxid).cat.catid
  3437. preSelectedCatid = [catid]
  3438. preSelectedBoxid = [boxid]
  3439. catidDisabled = True
  3440. boxidDisabled = True
  3441. else:
  3442. self.help = 'Select an IP device and one or more service ' +\
  3443. 'handlers'
  3444. preSelectedCatid = []
  3445. preSelectedBoxid = []
  3446. catidDisabled = False
  3447. boxidDisabled = False
  3448. lb = None
  3449. lb2 = None
  3450. if req:
  3451. # Catid select
  3452. select1 = simpleSelect('Category',
  3453. 'cn_catid',
  3454. ('catid,descr',
  3455. 'cat',
  3456. None,
  3457. None,
  3458. 'catid'),
  3459. preSelectedCatid,
  3460. optionFormat='$1',
  3461. selectMultiple=False,
  3462. multipleHeight=8,
  3463. disabled=catidDisabled)
  3464. # Ip device select
  3465. select2 = updateSelect(select1,
  3466. 'catid',
  3467. 'IP device',
  3468. 'boxid',
  3469. ('netboxid,sysname',
  3470. 'netbox',
  3471. None,
  3472. None,
  3473. 'sysname'),
  3474. preSelectedBoxid,
  3475. optionFormat='$2',
  3476. actionOnChange="toggleDisabled" +\
  3477. "(this,'handler');",
  3478. selectMultiple=False,
  3479. multipleHeight=8,
  3480. disabled=boxidDisabled)
  3481. # Handler select
  3482. # Get checkers (handler names)
  3483. for c in getCheckers():
  3484. presentHandlers.append(c)
  3485. handlerName = c
  3486. if presentHandlers.count(c) > 1:
  3487. handlerName = c + ' (' + str(presentHandlers.count(c))+\
  3488. ')'
  3489. handlerEntries.append((c,handlerName,False))
  3490. handlerEntries.sort()
  3491. handlerDisabled = True
  3492. if req.form.has_key('boxid') or editId:
  3493. handlerDisabled = False
  3494. select3 = simpleSelect('Handler',
  3495. 'handler',
  3496. None,
  3497. addEntryList=handlerEntries,
  3498. actionOnChange="updateDisplay" +\
  3499. "(this);",
  3500. selectMultiple=True,
  3501. multipleHeight=8,
  3502. disabled=handlerDisabled)
  3503. st = selectTree()
  3504. st.addSelect(select1)
  3505. st.addSelect(select2)
  3506. st.update(req.form)
  3507. st2 = selectTree()
  3508. st2.addSelect(select3)
  3509. st2.update(req.form)
  3510. lb = selectTreeLayoutBox(showTitles=False)
  3511. lb.addSelect(select1)
  3512. lb.addSelect(select2)
  3513. lb2 = selectTreeLayoutBox(showEmptySelects=True,
  3514. showTitles=False)
  3515. lb2.addSelect(select3)
  3516. # Make all the serviceproperty boxes (they are hidden until
  3517. # displayed by javascript)
  3518. # Remember selected boxes
  3519. selectedHandlers = []
  3520. if form.has_key('handler'):
  3521. selectedHandlers = form['handler']
  3522. if not type(selectedHandlers) is list:
  3523. selectedHandlers = [selectedHandlers]
  3524. propertyList = []
  3525. for checker in getCheckers():
  3526. prop = self.makePropertyInput(checker,form,selectedHandlers)
  3527. if prop:
  3528. propertyList.append(prop)
  3529. propertyList = propertyListFilled + propertyList
  3530. f = {'boxid': [inputTreeSelect(treeselect=lb),
  3531. REQ_TRUE,'IP device',FIELD_STRING],
  3532. 'handler': [inputTreeSelect(treeselect=lb2),
  3533. REQ_TRUE,'Handler',FIELD_STRING],
  3534. 'properties': [inputServiceProperties(propertyList),
  3535. REQ_FALSE,'Properties',FIELD_STRING]}
  3536. self.fields = f
  3537. self.setControlNames()
  3538. def makePropertyInput(self,handler,form,selectedHandlers=None,
  3539. serviceId=None):
  3540. """ Used by init to make property inputs
  3541. handler = handler/checker name (string)
  3542. form = form/value (dict)
  3543. selectedHandler = list of handlers already selected in add
  3544. serviceId = fill fields with this service """
  3545. properties = getDescription(handler)
  3546. propertyInput = None
  3547. args = {}
  3548. optargs = {}
  3549. if properties:
  3550. title = "Properties for " + handler + ' (' +\
  3551. properties['description']+ ')'
  3552. if properties.has_key('args'):
  3553. i = 0
  3554. for a in properties['args']:
  3555. textInput = inputText()
  3556. if serviceId:
  3557. name = handler + '_' + serviceId + '_' + str(i)
  3558. else:
  3559. name = handler + '_' + str(i)
  3560. textInput.name = name
  3561. if form.has_key(name):
  3562. textInput.value = form[name]
  3563. elif form.has_key(a):
  3564. # When editing, form is replaced by
  3565. # propertyValues which is a dict with
  3566. # the real propertyy names (not the form names)
  3567. textInput.value = form[a]
  3568. args[a] = [textInput,REQ_TRUE]
  3569. i += 1
  3570. if properties.has_key('optargs'):
  3571. i = 0
  3572. for a in properties['optargs']:
  3573. textInput = inputText()
  3574. if serviceId:
  3575. name = handler + '_' + serviceId + '_opt_' + str(i)
  3576. else:
  3577. name = handler + '_opt_' + str(i)
  3578. textInput.name = name
  3579. if form.has_key(name):
  3580. textInput.value = form[name]
  3581. elif form.has_key(a):
  3582. textInput.value = form[a]
  3583. optargs[a] = [textInput,REQ_FALSE]
  3584. i += 1
  3585. if len(args) or len(optargs):
  3586. if serviceId:
  3587. id = handler + '_' + serviceId
  3588. else:
  3589. id = handler
  3590. if type(selectedHandlers) is list:
  3591. if (id in selectedHandlers):
  3592. # This property was already selected, so show
  3593. # the property box
  3594. display = True
  3595. else:
  3596. display = False
  3597. else:
  3598. # No list of selected handlers given
  3599. # (for making edit page)
  3600. display = True
  3601. propertyInput = inputServiceProperty(title,id,args,optargs,
  3602. display)
  3603. return propertyInput
  3604. def checkRequiredProperties(self,handler,form,serviceId=None):
  3605. """ Check if all the required properties of handler is present
  3606. in the form data.
  3607. handler = handler/checker name (string)
  3608. form = form data dict
  3609. serviceId = id to append to field names if we're editing (string)"""
  3610. properties = getDescription(handler)
  3611. if properties:
  3612. if properties.has_key('args'):
  3613. i = 0
  3614. for arg in properties['args']:
  3615. if serviceId:
  3616. requiredArg = handler + '_' + serviceId + '_' + str(i)
  3617. else:
  3618. requiredArg = handler + '_' + str(i)
  3619. missing = False
  3620. if not form.has_key(requiredArg):
  3621. missing = True
  3622. else:
  3623. if not len(form[requiredArg]):
  3624. missing = True
  3625. if missing:
  3626. raise("Missing required field '" + arg + "'" +\
  3627. " for handler " + handler)
  3628. i += 1
  3629. def insertProperties(self,handler,form,serviceId,editing=False):
  3630. """ Insert service properties for a serviceId from form data.
  3631. handler = service handler (string)
  3632. form = form data (dict)
  3633. serviceId = service id the properties belong to (string)
  3634. editing = if we're editing then add serviceId to field names """
  3635. properties = getDescription(handler)
  3636. if properties:
  3637. if properties.has_key('args'):
  3638. i = 0
  3639. for property in properties['args']:
  3640. # Already know that all properties in 'args'
  3641. # are present
  3642. if editing:
  3643. propertyName = handler + '_' + serviceId + '_' + str(i)
  3644. else:
  3645. propertyName = handler + '_' + str(i)
  3646. fields = {'serviceid': serviceId,
  3647. 'property': property,
  3648. 'value': form[propertyName]}
  3649. addEntryFields(fields,
  3650. 'serviceproperty')
  3651. i += 1
  3652. if properties.has_key('optargs'):
  3653. i = 0
  3654. for property in properties['optargs']:
  3655. # optargs are optional, must
  3656. # check if they are present
  3657. if editing:
  3658. propertyName = handler + '_' + serviceId + '_opt_' +\
  3659. str(i)
  3660. else:
  3661. propertyName = handler + '_opt_' + str(i)
  3662. if form.has_key(propertyName):
  3663. if len(form[propertyName]):
  3664. fields = {'serviceid': serviceId,
  3665. 'property': property,
  3666. 'value': form[propertyName]}
  3667. addEntryFields(fields,
  3668. 'serviceproperty')
  3669. i += 1
  3670. def add(self,req,templateForm,action):
  3671. """ Adds a service entry. Overrides the default add function of
  3672. seeddbPage. """
  3673. action = 'add'
  3674. status = seeddbStatus()
  3675. form = req.form
  3676. try:
  3677. if not form.has_key('boxid'):
  3678. raise("Missing required field 'IP device'")
  3679. if not form.has_key('handler'):
  3680. raise("Missing required field 'handler'")
  3681. # Make list of selected handlers
  3682. selectedHandlers = form['handler']
  3683. if not type(selectedHandlers) is list:
  3684. selectedHandlers = [selectedHandlers]
  3685. # Check if all required properties are present
  3686. for handler in selectedHandlers:
  3687. self.checkRequiredProperties(handler,form)
  3688. # Add new services
  3689. for handler in selectedHandlers:
  3690. # Add service entry
  3691. fields = {'netboxid': form['boxid'],
  3692. 'handler': handler}
  3693. serviceId = addEntryFields(fields,self.tableName,
  3694. (self.tableIdKey,self.sequence))
  3695. # Add serviceproperty entries
  3696. self.insertProperties(handler,form,serviceId)
  3697. action = 'list'
  3698. status.messages.append('Added service: ' +\
  3699. self.describe(serviceId))
  3700. except:
  3701. status.errors.append(str(sys.exc_info()[0]))
  3702. return (status,action,templateForm,None)
  3703. def update(self,req,templateForm,selected):
  3704. """ Updates service entries. Overrides the default update function
  3705. in seeddbPage """
  3706. action = 'edit'
  3707. status = seeddbStatus()
  3708. editId = selected[0]
  3709. form = req.form
  3710. # Get selected handlers
  3711. selectedHandlers = []
  3712. if form.has_key('handler'):
  3713. if len(form['handler']):
  3714. selectedHandlers = form['handler']
  3715. if not type(selectedHandlers) is list:
  3716. selectedHandlers = [selectedHandlers]
  3717. try:
  3718. # editId is serviceid, get netboxid
  3719. boxid = str(nav.db.manage.Service(editId).netbox.netboxid)
  3720. sql = "SELECT serviceid FROM service WHERE netboxid='" +\
  3721. boxid + "' ORDER BY handler"
  3722. result = executeSQLreturn(sql)
  3723. # get all serviceid's for this netbox
  3724. serviceIdList = []
  3725. if result:
  3726. for row in result:
  3727. serviceIdList.append(str(row[0]))
  3728. stillSelectedHandlers = []
  3729. deselectedHandlers = []
  3730. currentHandlerIds = []
  3731. for serviceId in serviceIdList:
  3732. handler = nav.db.manage.Service(serviceId).handler
  3733. handlerId = handler + '_' + serviceId
  3734. currentHandlerIds.append(handlerId)
  3735. if handlerId in selectedHandlers:
  3736. stillSelectedHandlers.append(serviceId)
  3737. else:
  3738. deselectedHandlers.append(serviceId)
  3739. addHandlers = []
  3740. for handlerId in selectedHandlers:
  3741. # Remove selected handlers which were already present
  3742. # when starting editing. selectedHandlers should contain
  3743. # only new handlers for adding after this
  3744. if not (handlerId in currentHandlerIds):
  3745. addHandlers.append(handlerId)
  3746. # Check for all required properties for old services
  3747. for serviceId in stillSelectedHandlers:
  3748. handler = nav.db.manage.Service(serviceId).handler
  3749. self.checkRequiredProperties(handler,form,serviceId)
  3750. # Check for all required properties for new services
  3751. for handler in addHandlers:
  3752. self.checkRequiredProperties(handler,form)
  3753. # Update properties for old services
  3754. for serviceId in stillSelectedHandlers:
  3755. # Delete all old serviceproperties for this serviceId,
  3756. handler = nav.db.manage.Service(serviceId).handler
  3757. sql = "DELETE FROM serviceproperty WHERE serviceid='" +\
  3758. serviceId + "'"
  3759. executeSQL([sql])
  3760. # Insert new serviceproperties
  3761. self.insertProperties(handler,form,serviceId,editing=True)
  3762. status.messages.append('Updated service: ' +\
  3763. self.describe(serviceId))
  3764. # Add new services
  3765. for handler in addHandlers:
  3766. fields = {'netboxid': boxid,
  3767. 'handler': handler}
  3768. serviceId = addEntryFields(fields,self.tableName,
  3769. (self.tableIdKey,self.sequence))
  3770. # Add serviceproperty entries
  3771. self.insertProperties(handler,form,serviceId)
  3772. status.messages.append('Added service: ' +\
  3773. self.describe(serviceId))
  3774. # Delete deselected services
  3775. for serviceId in deselectedHandlers:
  3776. status.messages.append('Deleted service: ' +\
  3777. self.describe(serviceId))
  3778. sql = "DELETE FROM service WHERE serviceid='" + serviceId + "'"
  3779. executeSQL([sql])
  3780. action = 'list'
  3781. except:
  3782. status.errors.append(str(sys.exc_info()[0]))
  3783. return (status,action,templateForm,selected)
  3784. def updateOLD(self):
  3785. action = 'edit'
  3786. status = seeddbStatus()
  3787. editId = req.form[UPDATE_ENTRY]
  3788. # Check if all required serviceproperties are present
  3789. handler = nav.db.manage.Service(editId).handler
  3790. properties = getDescription(handler)
  3791. missing = False
  3792. propertyStep = False
  3793. if properties:
  3794. if properties.has_key('args'):
  3795. for required in properties['args']:
  3796. if not req.form.has_key(required):
  3797. propertyStep = True
  3798. if req.form.has_key(required):
  3799. if not len(req.form[required]):
  3800. missing = required
  3801. propertyStep = True
  3802. if properties.has_key('optargs'):
  3803. for optional in properties['optargs']:
  3804. if not req.form.has_key(optional):
  3805. # If optargs are missing, then we haven't reached
  3806. # the add property step yet
  3807. propertyStep = True
  3808. if propertyStep or (missing and len(req.form['netboxid'])):
  3809. if missing:
  3810. # Missing required property
  3811. status.errors.append("Missing required property '" +\
  3812. missing + "'")
  3813. checker = handler
  3814. templateForm.title = 'Edit service'
  3815. templateForm.add(self.editbox(self,formData=req.form,
  3816. editId=selected[0],disabled=True))
  3817. templateForm.add(self.editboxServiceProperties(checker,
  3818. req.form['netboxid'],
  3819. editId=selected[0]))
  3820. action = 'continueEdit'
  3821. elif not missing:
  3822. # Update service entry
  3823. fields = {'netboxid': req.form['netboxid'],
  3824. 'handler': handler}
  3825. updateEntryFields(fields,'service','serviceid',editId)
  3826. # Update serviceproperty entries
  3827. if properties:
  3828. # Delete old properties
  3829. deleteEntry([editId],'serviceproperty','serviceid')
  3830. if properties.has_key('args'):
  3831. for property in properties['args']:
  3832. # Already know that all properties in 'args' are present
  3833. fields = {'serviceid': editId,
  3834. 'property': property,
  3835. 'value': req.form[property]}
  3836. addEntryFields(fields,'serviceproperty')
  3837. action = 'list'
  3838. if properties.has_key('optargs'):
  3839. for property in properties['optargs']:
  3840. # optargs are optional, must check if they are present
  3841. if req.form.has_key(property):
  3842. if len(req.form[property]):
  3843. fields = {'serviceid': editId,
  3844. 'property': property,
  3845. 'value': req.form[property]}
  3846. addEntryFields(fields,'serviceproperty')
  3847. action = 'list'
  3848. else:
  3849. status.errors.append("Missing required property '" + missing+"'")
  3850. action = 'list'
  3851. selected = [editId]
  3852. return (status,action,templateForm,selected)
  3853. class pageSnmpoid(seeddbPage):
  3854. """ Describes adding to the snmpoid table. """
  3855. basePath = BASEPATH + 'snmpoid/'
  3856. table = nav.db.manage.Snmpoid
  3857. pageName = 'snmpoid'
  3858. tableName = 'snmpoid'
  3859. tableIdKey = 'snmpoidid'
  3860. sequence = 'snmpoid_snmpoidid_seq'
  3861. editMultipleAllowed = False
  3862. editIdAllowed = False
  3863. # Description format used by describe(id)
  3864. descriptionFormat = [('','snmpoid')]
  3865. # Unique fields (for errormessages from add/update)
  3866. unique = 'oidkey'
  3867. # Nouns
  3868. singular = 'snmpoid'
  3869. plural = 'snmpoids'
  3870. # Delete dependencies
  3871. # delete not allowed for snmpoid, so no dependencies
  3872. pathAdd = EDITPATH + [('Snmpoid',False),('Add',False)]
  3873. class listDef(entryList):
  3874. """ Dummy class. No list view defined for for the snmpoid page. """
  3875. pass
  3876. class editbox(editbox):
  3877. """ Describes fields for adding snmpoid entries.
  3878. The template uses this field information to display the form. """
  3879. def __init__(self,page,req=None,editId=None,formData=None):
  3880. self.page = page.pageName
  3881. self.table = page.table
  3882. self.hiddenFields = {}
  3883. disabled = False
  3884. if editId:
  3885. disabled = True
  3886. f = {'oidkey': [inputText(disabled=disabled),REQ_TRUE,'OID key',
  3887. FIELD_STRING],
  3888. 'snmpoid': [inputText(),REQ_TRUE,'Snmpoid',FIELD_STRING],
  3889. 'descr': [inputText(),REQ_FALSE,'Description',FIELD_STRING],
  3890. 'oidsource': [inputText(),REQ_FALSE,'OID source',FIELD_STRING],
  3891. 'match_regex': [inputText(),REQ_FALSE,'Match regexp',
  3892. FIELD_STRING],
  3893. 'oidname': [inputText(),REQ_FALSE,'OID name',FIELD_STRING],
  3894. 'mib': [inputText(),REQ_FALSE,'MIB',FIELD_STRING]}
  3895. self.fields = f
  3896. self.setControlNames()
  3897. if editId:
  3898. self.editId = editId
  3899. self.fill()
  3900. if formData:
  3901. self.formFill(formData)
  3902. if disabled:
  3903. self.addDisabled()
  3904. def add(self,req,outputForm,action):
  3905. """ Dummy function which calls seeddbPage.add and then sets action
  3906. to redirect (to the menu page). """
  3907. status,action,outputForm,addedId = seeddbPage.add(self,req,
  3908. outputForm,action)
  3909. action = 'redirect'
  3910. return (status,action,outputForm,addedId)
  3911. class pageSubcat(seeddbPage):
  3912. """ Describes editing of the subcat table. """
  3913. basePath = BASEPATH + 'subcat/'
  3914. table = nav.db.manage.Subcat
  3915. pageName = 'subcat'
  3916. tableName = 'subcat'
  3917. tableIdKey = 'subcatid'
  3918. sequence = None
  3919. editMultipleAllowed = True
  3920. editIdAllowed = True
  3921. # Description format used by describe(id)
  3922. descriptionFormat = [('','subcatid'),
  3923. (' (','descr'),
  3924. (')',None)]
  3925. # Unique fields (for errormessages from add/update)
  3926. unique = 'subcatid'
  3927. # Nouns
  3928. singular = 'subcategory'
  3929. plural = 'subcategories'
  3930. # Delete dependencies
  3931. dependencies = []
  3932. pathAdd = EDITPATH + [('Subcategory',basePath+'list'),('Add',False)]
  3933. pathEdit = EDITPATH + [('Subcategory',basePath+'list'),('Edit',False)]
  3934. pathDelete = EDITPATH + [('Subcategory',basePath+'list'),('Delete',False)]
  3935. pathList = EDITPATH + [('Subcategory',False)]
  3936. class listDef(entryList):
  3937. """ Describes subcat list view """
  3938. def __init__(self,req,struct,sort,deleteWhere=None):
  3939. # Do general init
  3940. entryList.__init__(self,req,struct,sort,deleteWhere)
  3941. # Specific init
  3942. self.defaultSortBy = 1
  3943. # list of (heading text, show sortlink, compare function for sort)
  3944. self.headingDefinition = [('Select',False,None),
  3945. ('Subcategory',True,None),
  3946. ('Parent category',True,None),
  3947. ('Description',True,None)]
  3948. self.cellDefinition = [(('subcatid,subcatid,catid,descr',
  3949. 'subcat',
  3950. None,
  3951. None,
  3952. 'catid,subcatid'),
  3953. [(None,None,entryListCell.CHECKBOX,None,None),
  3954. (1,'{p}edit/{id}',None,None,None),
  3955. (2,None,None,None,None),
  3956. (3,None,None,None,None)])]
  3957. class editbox(editbox):
  3958. """ Describes fields for adding and editing patch entries.
  3959. The template uses this field information to display the form. """
  3960. def __init__(self,page,req=None,editId=None,formData=None):
  3961. self.page = page.pageName
  3962. self.table = page.table
  3963. o = [('','Select a category')]
  3964. for cat in nav.db.manage.Cat.getAllIterator():
  3965. o.append((cat.catid,cat.catid + ' (' + cat.descr + ')'))
  3966. f = {'subcatid': [inputText(),REQ_TRUE,'Subcategory',FIELD_STRING],
  3967. 'catid': [inputSelect(options=o),REQ_TRUE,'Category',
  3968. FIELD_STRING],
  3969. 'descr': [inputText(),REQ_TRUE,'Description',FIELD_STRING]}
  3970. self.fields = f
  3971. self.setControlNames()
  3972. if editId:
  3973. self.editId = editId
  3974. self.fill()
  3975. if formData:
  3976. self.formFill(formData)
  3977. class pageType(seeddbPage):
  3978. """ Describes editing of the type table. """
  3979. basePath = BASEPATH + 'type/'
  3980. table = nav.db.manage.Type
  3981. pageName = 'type'
  3982. tableName = 'type'
  3983. tableIdKey = 'typeid'
  3984. sequence = 'type_typeid_seq'
  3985. editMultipleAllowed = True
  3986. editIdAllowed = False
  3987. # Description format used by describe(id)
  3988. descriptionFormat = [('','vendor.vendorid'),
  3989. (' ','typename')]
  3990. # Unique fields (for errormessages from add/update)
  3991. unique = 'sysobjectid'
  3992. # Nouns
  3993. singular = 'type'
  3994. plural = 'types'
  3995. # Delete dependencies
  3996. dependencies = []
  3997. pathAdd = EDITPATH + [('Type',basePath+'list'),('Add',False)]
  3998. pathEdit = EDITPATH + [('Type',basePath+'list'),('Edit',False)]
  3999. pathDelete = EDITPATH + [('Type',basePath+'list'),('Delete',False)]
  4000. pathList = EDITPATH + [('Type',False)]
  4001. class listDef(entryList):
  4002. """ Describes room list view """
  4003. def __init__(self,req,struct,sort,deleteWhere=None):
  4004. # Do general init
  4005. entryList.__init__(self,req,struct,sort,deleteWhere)
  4006. # Specific init
  4007. self.defaultSortBy = 1
  4008. # list of (heading text, show sortlink, compare function for sort)
  4009. self.headingDefinition = [('Select',False,None),
  4010. ('Vendor',True,None),
  4011. ('Typename',True,None),
  4012. ('Description',True,None),
  4013. ('Sysobjectid',True,None),
  4014. ('Frequency',True,None),
  4015. ('cdp',True,None),
  4016. ('tftp',True,None)]
  4017. self.cellDefinition = [(('typeid,vendorid,typename,descr,' +\
  4018. 'sysobjectid,frequency,' +\
  4019. 'CASE WHEN cdp THEN \'yes\' ' +\
  4020. 'ELSE \'no\' END,' +\
  4021. 'CASE WHEN tftp THEN \'yes\' ' +\
  4022. 'ELSE \'no\' END',
  4023. 'type',
  4024. None,
  4025. None,
  4026. 'vendorid,typename'),
  4027. [(None,None,entryListCell.CHECKBOX,None,None),
  4028. (1,None,None,None,None),
  4029. (2,'{p}edit/{id}',None,None,None),
  4030. (3,None,None,None,None),
  4031. (4,None,None,None,None),
  4032. (5,None,None,None,None),
  4033. (6,None,None,None,None),
  4034. (7,None,None,None,None)])]
  4035. class editbox(editbox):
  4036. """ Describes fields for adding and editing type entries.
  4037. The template uses this field information to display the form. """
  4038. def __init__(self,page,req=None,editId=None,formData=None):
  4039. self.page = page.pageName
  4040. self.table = page.table
  4041. # Field definitions {field name: [input object, required]}
  4042. f = {'typename': [inputText(),REQ_TRUE,'Typename',FIELD_STRING],
  4043. 'vendorid': [inputSelect(table=seeddbTables.seeddbVendor),
  4044. REQ_TRUE,'Vendor',FIELD_STRING],
  4045. 'descr': [inputText(),REQ_TRUE,'Description',FIELD_STRING],
  4046. 'sysobjectid': [inputText(),REQ_TRUE,'Sysobjectid',
  4047. FIELD_STRING],
  4048. 'cdp': [inputCheckbox(),REQ_NONEMPTY,'cdp',FIELD_STRING],
  4049. 'tftp': [inputCheckbox(),REQ_NONEMPTY,'tftp',FIELD_STRING],
  4050. 'frequency': [inputText(),REQ_NONEMPTY,'frequency',
  4051. FIELD_INTEGER]}
  4052. self.fields = f
  4053. self.setControlNames()
  4054. if editId:
  4055. self.editId = editId
  4056. self.fill()
  4057. if formData:
  4058. self.formFill(formData)
  4059. class pageUsage(seeddbPage):
  4060. """ Describes editing of the usage table. """
  4061. basePath = BASEPATH + 'usage/'
  4062. table = nav.db.manage.Usage
  4063. pageName = 'usage'
  4064. tableName = 'usage'
  4065. tableIdKey = 'usageid'
  4066. sequence = None
  4067. editMultipleAllowed = True
  4068. editIdAllowed = True
  4069. # Description format used by describe(id)
  4070. descriptionFormat = [('','usageid'),
  4071. (' (','descr'),
  4072. (')',None)]
  4073. # Unique fields (for errormessages from add/update)
  4074. unique = 'usageid'
  4075. # Nouns
  4076. singular = 'usage category'
  4077. plural = 'usage categories'
  4078. # Delete dependencies
  4079. dependencies = []
  4080. pathAdd = EDITPATH + [('Usage',basePath+'list'),('Add',False)]
  4081. pathEdit = EDITPATH + [('Usage',basePath+'list'),('Edit',False)]
  4082. pathDelete = EDITPATH + [('Usage',basePath+'list'),('Delete',False)]
  4083. pathList = EDITPATH + [('Usage',False)]
  4084. class listDef(entryList):
  4085. """ Describes usage list view """
  4086. def __init__(self,req,struct,sort,deleteWhere=None):
  4087. # Do general init
  4088. entryList.__init__(self,req,struct,sort,deleteWhere)
  4089. # Specific init
  4090. self.defaultSortBy = 1
  4091. # list of (heading text, show sortlink, compare function for sort)
  4092. self.headingDefinition = [('Select',False,None),
  4093. ('Usage category',True,None),
  4094. ('Description',True,None)]
  4095. self.cellDefinition = [(('usageid,usageid,descr',
  4096. 'usage',
  4097. None,
  4098. None,
  4099. 'usageid'),
  4100. [(None,None,entryListCell.CHECKBOX,None,None),
  4101. (1,'{p}edit/{id}',None,None,None),
  4102. (2,None,None,None,None)])]
  4103. class editbox(editbox):
  4104. """ Describes fields for adding and editing patch entries.
  4105. The template uses this field information to display the form. """
  4106. def __init__(self,page,req=None,editId=None,formData=None):
  4107. self.page = page.pageName
  4108. self.table = page.table
  4109. # Field definitions {field name: [input object, required]}
  4110. f = {'usageid': [inputText(maxlength=30),REQ_TRUE,
  4111. 'Usage category',FIELD_STRING],
  4112. 'descr': [inputText(),REQ_TRUE,'Description',FIELD_STRING]}
  4113. self.fields = f
  4114. self.setControlNames()
  4115. if editId:
  4116. self.editId = editId
  4117. self.fill()
  4118. if formData:
  4119. self.formFill(formData)
  4120. class pageVendor(seeddbPage):
  4121. """ Describes editing of the vendor table. """
  4122. basePath = BASEPATH + 'vendor/'
  4123. table = nav.db.manage.Vendor
  4124. pageName = 'vendor'
  4125. tableName = 'vendor'
  4126. tableIdKey = 'vendorid'
  4127. sequence = None
  4128. editMultipleAllowed = True
  4129. editIdAllowed = True
  4130. # Description format used by describe(id)
  4131. descriptionFormat = [('','vendorid')]
  4132. # Unique fields (for errormessages from add/update)
  4133. unique = 'vendorid'
  4134. # Nouns
  4135. singular = 'vendor'
  4136. plural = 'vendors'
  4137. # Delete dependencies
  4138. dependencies = [(nav.db.manage.Type,
  4139. 'types',
  4140. 'vendorid',
  4141. '/report/type/?vendorid='),
  4142. (nav.db.manage.Product,
  4143. 'products',
  4144. 'vendorid',
  4145. '/report/product/?vendorid=')]
  4146. pathAdd = EDITPATH + [('Vendor',basePath+'list'),('Add',False)]
  4147. pathEdit = EDITPATH + [('Vendor',basePath+'list'),('Edit',False)]
  4148. pathDelete = EDITPATH + [('Vendor',basePath+'list'),('Delete',False)]
  4149. pathList = EDITPATH + [('Vendor',False)]
  4150. class listDef(entryList):
  4151. """ Describes vendor list view """
  4152. def __init__(self,req,struct,sort,deleteWhere=None):
  4153. # Do general init
  4154. entryList.__init__(self,req,struct,sort,deleteWhere)
  4155. # Specific init
  4156. self.defaultSortBy = 1
  4157. # list of (heading text, show sortlink, compare function for sort)
  4158. self.headingDefinition = [('Select',False,None),
  4159. ('Vendor',True,None)]
  4160. self.cellDefinition = [(('vendorid,vendorid',
  4161. 'vendor',
  4162. None,
  4163. None,
  4164. 'vendorid'),
  4165. [(None,None,entryListCell.CHECKBOX,None,None),
  4166. (1,'{p}edit/{id}',None,None,None)])]
  4167. class editbox(editbox):
  4168. """ Describes fields for adding and editing vendor entries.
  4169. The template uses this field information to display the form. """
  4170. def __init__(self,page,req=None,editId=None,formData=None):
  4171. self.page = page.pageName
  4172. self.table = page.table
  4173. # Field definitions {field name: [input object, required]}
  4174. f = {'vendorid': [inputText(maxlength=15),REQ_TRUE,'Vendor',
  4175. FIELD_STRING]}
  4176. self.fields = f
  4177. self.setControlNames()
  4178. if editId:
  4179. self.editId = editId
  4180. self.fill()
  4181. if formData:
  4182. self.formFill(formData)
  4183. class pageVlan(seeddbPage):
  4184. """ Describes editing of the vlan table (no adding or
  4185. deleting allowed). """
  4186. basePath = BASEPATH + 'vlan/'
  4187. table = nav.db.manage.Vlan
  4188. pageName = 'vlan'
  4189. tableName = 'vlan'
  4190. tableIdKey = 'vlanid'
  4191. sequence = 'vlan_vlanid_seq'
  4192. editMultipleAllowed = True
  4193. editIdAllowed = False
  4194. # Disallow adding
  4195. disallowAdd = True
  4196. disallowAddReason = 'Cannot add vlans manually'
  4197. # Description format used by describe(id)
  4198. descriptionFormat = [('','nettype'),
  4199. (' ','vlan')]
  4200. # Unique fields (for errormessages from add/update)
  4201. unique = ''
  4202. # Nouns
  4203. singular = 'vlan'
  4204. plural = 'vlans'
  4205. # Delete dependencies
  4206. dependencies = []
  4207. pathAdd = EDITPATH + [('Vlan',basePath+'list'),('Add',False)]
  4208. pathEdit = EDITPATH + [('Vlan',basePath+'list'),('Edit',False)]
  4209. pathDelete = EDITPATH + [('Vlan',basePath+'list'),('Delete',False)]
  4210. pathList = EDITPATH + [('Vlan',False)]
  4211. class listDef(entryList):
  4212. """ Describes vlan list view """
  4213. def __init__(self,req,struct,sort,deleteWhere=None):
  4214. # Do general init
  4215. entryList.__init__(self,req,struct,sort,deleteWhere)
  4216. # Specific init
  4217. self.defaultSortBy = 1
  4218. # list of (heading text, show sortlink, compare function for sort)
  4219. self.headingDefinition = [('Select',False,None),
  4220. ('Vlan',True,None),
  4221. ('Nettype',True,None),
  4222. ('Organisation',True,None),
  4223. ('Usage',True,None),
  4224. ('Netident',True,None),
  4225. ('Description',True,None),
  4226. ('Prefixes',False,None)]
  4227. prefixSQL = "SELECT netaddr FROM prefix WHERE VLANID={id}"
  4228. self.cellDefinition = [(('vlanid,vlan,nettype,orgid,usageid,' +\
  4229. 'netident,description',
  4230. 'vlan',
  4231. None,
  4232. None,
  4233. 'vlan,nettype,orgid'),
  4234. [(None,None,entryListCell.CHECKBOX,None,None),
  4235. (1,'{p}edit/{id}',None,None,None),
  4236. (2,'{p}edit/{id}',None,None,None),
  4237. (3,None,None,None,None),
  4238. (4,None,None,None,None),
  4239. (5,None,None,None,None),
  4240. (6,None,None,None,None),
  4241. (prefixSQL,None,None,None,None)])]
  4242. class editbox(editbox):
  4243. """ Describes fields for adding and editing vlan entries.
  4244. The template uses this field information to display the form. """
  4245. def __init__(self,page,req=None,editId=None,formData=None):
  4246. self.page = page.pageName
  4247. self.table = page.table
  4248. # Available nettypes (should be in the database)
  4249. nettypes = [('core','core'),
  4250. ('elink','elink'),
  4251. ('lan','lan'),
  4252. ('link','link'),
  4253. ('loopback','loopback'),
  4254. ('private','private')]
  4255. orgs = [('','No organisation')]
  4256. for o in nav.db.manage.Org.getAllIterator():
  4257. orgs.append((o.orgid,o.orgid + ' (' + str(o.descr) + ')'))
  4258. usageids = [('','No usage')]
  4259. for usage in nav.db.manage.Usage.getAllIterator():
  4260. usageids.append((usage.usageid,usage.usageid + ' (' + \
  4261. usage.descr + ')'))
  4262. # Field definitions {field name: [input object, required]}
  4263. f = {'nettype': [inputSelect(options=nettypes),REQ_TRUE,
  4264. 'Nettype',FIELD_STRING],
  4265. 'orgid': [inputSelect(options=orgs),REQ_FALSE,
  4266. 'Organisation',FIELD_STRING],
  4267. 'netident': [inputText(),REQ_FALSE,'Netident',FIELD_STRING],
  4268. 'description': [inputText(),REQ_FALSE,'Description',
  4269. FIELD_STRING],
  4270. 'vlan': [inputText(size=5),REQ_FALSE,'Vlan',FIELD_INTEGER],
  4271. 'usageid': [inputSelect(options=usageids),REQ_FALSE,
  4272. 'Usage',FIELD_STRING]}
  4273. self.fields = f
  4274. self.setControlNames()
  4275. if editId:
  4276. self.editId = editId
  4277. self.fill()
  4278. if formData:
  4279. self.formFill(formData)
  4280. # List of seeddb pages
  4281. pageList = {'cabling': pageCabling,
  4282. 'location': pageLocation,
  4283. 'netbox': pageNetbox,
  4284. 'org': pageOrg,
  4285. 'patch': pagePatch,
  4286. 'prefix': pagePrefix,
  4287. 'product': pageProduct,
  4288. 'room': pageRoom,
  4289. 'service': pageService,
  4290. 'snmpoid': pageSnmpoid,
  4291. 'subcat': pageSubcat,
  4292. 'type': pageType,
  4293. 'usage': pageUsage,
  4294. 'vendor': pageVendor,
  4295. 'vlan': pageVlan}
  4296. ######################
  4297. ##
  4298. ## Bulk import classes
  4299. ##
  4300. ########################
  4301. class editboxBulk(editbox):
  4302. """ Editbox used by the template to disaply the main bulk import page. """
  4303. page = 'bulk'
  4304. help = 'Import multiple entries by selecting a file, or pasting ' +\
  4305. 'into the textarea. Select an import type to see syntax ' + \
  4306. 'for this type.'
  4307. def __init__(self):
  4308. tables = [('','Select an import type'),
  4309. ('location','Locations'),
  4310. ('room','Rooms'),
  4311. ('org','Organisations'),
  4312. ('usage','Usage categories'),
  4313. ('subcat','Subcategories'),
  4314. ('type','Types'),
  4315. ('product','Products'),
  4316. ('vendor','Vendors'),
  4317. ('netbox','IP devices'),
  4318. ('service','Services'),
  4319. ('vlan','Vlans'),
  4320. ('prefix','Prefixes'),
  4321. ('cabling','Cabling'),
  4322. ('patch','Patch')]
  4323. sep = [(':','Colon (:)'),
  4324. (';','Semicolon (;)'),
  4325. (',','Comma (,)')]
  4326. f = {'table': [inputSelect(options=tables),REQ_FALSE],
  4327. 'separator': [inputSelect(options=sep),REQ_FALSE],
  4328. 'file': [inputFile(),REQ_FALSE],
  4329. 'textarea': [inputTextArea(),REQ_FALSE]}
  4330. self.fields = f
  4331. self.setControlNames()
  4332. def bulkImportParse(input,bulkdef,separator):
  4333. """ Parses a list of input data.
  4334. input = list of rows to import
  4335. bulkdef = bulk defintion class
  4336. separator = field separator chosen """
  4337. commentChar = '#'
  4338. # Any number of spaces followed by a # is a comment
  4339. comment = re.compile('\s*%s' % commentChar)
  4340. # list of (parsed correctly,data/error)
  4341. parsed = []
  4342. linenr = 0
  4343. for line in input:
  4344. linenr += 1
  4345. remark = None
  4346. if comment.match(line):
  4347. # This line is a comment
  4348. pass
  4349. elif len(line) > 0:
  4350. fields = line.split(separator)
  4351. data = {}
  4352. if (bulkdef.enforce_max_fields) and \
  4353. (len(fields) > bulkdef.max_num_fields):
  4354. # len(fields) > max_num_fields
  4355. # and enforce_max_fields == True
  4356. status = BULK_STATUS_RED_ERROR
  4357. remark = 'Too many fields'
  4358. elif len(fields) < bulkdef.min_num_fields:
  4359. status = BULK_STATUS_RED_ERROR
  4360. remark = 'Missing one or more required fields'
  4361. else:
  4362. status = BULK_STATUS_OK
  4363. excessCounter = 0
  4364. for i in range(0,len(fields)):
  4365. # Is this one of the predefined fields?
  4366. # ie. where i < max_fields
  4367. # if not, it is eg. a subcatfield
  4368. if i < bulkdef.max_num_fields:
  4369. # fieldname,maxlen,required,use
  4370. fn,ml,req,use = bulkdef.fields[i]
  4371. # missing required field?
  4372. if req and not len(fields[i]):
  4373. status = BULK_STATUS_RED_ERROR
  4374. remark = "Syntax error: Required field '"+ fn + \
  4375. "' missing"
  4376. break
  4377. # max field length exceeded?
  4378. if ml and (len(fields[i]) > ml):
  4379. status = BULK_STATUS_RED_ERROR
  4380. remark = "Syntax error: Field '" + fn + \
  4381. "' exceeds max field length"
  4382. break
  4383. else:
  4384. # This field isn't specified in the bulkdef
  4385. # Used by netbox for adding any number of subcats
  4386. fn = BULK_UNSPECIFIED_FIELDNAME + str(excessCounter)
  4387. excessCounter += 1
  4388. # check the validity of this field with the bulkdefs
  4389. # checkValidity function this is for checking things
  4390. # like: do ip resolve to a hostname for ip devices?
  4391. (status,validremark) = bulkdef.checkValidity(fn,fields[i])
  4392. if validremark:
  4393. remark = validremark
  4394. if status != BULK_STATUS_OK:
  4395. break
  4396. # Check the uniqueness of id fields _after_ validity checking
  4397. if i < bulkdef.max_num_fields and \
  4398. type(bulkdef.uniqueField) is str and \
  4399. fn == bulkdef.uniqueField:
  4400. where = bulkdef.uniqueField+"='"+fields[i] + "'"
  4401. result = bulkdef.table.getAllIDs(where=where)
  4402. if result:
  4403. status = BULK_STATUS_YELLOW_ERROR
  4404. remark = "Not unique: An entry with " +fn + \
  4405. "=" + fields[i] + " already exists"
  4406. break
  4407. # use this field if no error (status==BULK_STATUS_OK)
  4408. # and if it's marked to be used (use == true)
  4409. if fn=='serial' and status==BULK_STATUS_OK:
  4410. data[fn] = fields[i]
  4411. if (status == BULK_STATUS_OK) and (use == True):
  4412. data[fn] = fields[i]
  4413. # postCheck
  4414. if status == BULK_STATUS_OK:
  4415. validremark = False
  4416. if bulkdef.postCheck:
  4417. (status,validremark,data) = bulkdef.postCheck(data)
  4418. #if data.has_key('serial'):
  4419. # del(data['serial'])
  4420. if validremark:
  4421. remark = validremark
  4422. parsed.append((status,data,remark,line,linenr))
  4423. return parsed
  4424. def bulkImport(req,action):
  4425. """ Main bulk import function. Displays menu and starts parsing. """
  4426. # Cnames for hidden inputs
  4427. BULK_HIDDEN_DATA = 'blk_hd'
  4428. BULK_TABLENAME = 'blk_tbl'
  4429. BULK_SEPARATOR = 'blk_sep'
  4430. status = seeddbStatus()
  4431. # form
  4432. form = editForm()
  4433. form.status = status
  4434. form.action = BASEPATH + 'bulk/'
  4435. form.title = 'Bulk import'
  4436. form.textConfirm = 'Preview import'
  4437. form.enctype = 'multipart/form-data'
  4438. form.add(editboxBulk())
  4439. # list
  4440. list = None
  4441. help = "# Rows starting with a '#' are comments\n" + \
  4442. "# Select a file to import from, or write here\n" + \
  4443. "# For field syntax, select an import type\n"
  4444. # Dict with the different bulk definitions
  4445. bulkdef = {'patch': bulkdefPatch,
  4446. 'cabling': bulkdefCabling,
  4447. 'location': bulkdefLocation,
  4448. 'room': bulkdefRoom,
  4449. 'netbox': bulkdefNetbox,
  4450. 'org': bulkdefOrg,
  4451. 'usage': bulkdefUsage,
  4452. 'service': bulkdefService,
  4453. 'vendor': bulkdefVendor,
  4454. 'subcat': bulkdefSubcat,
  4455. 'type': bulkdefType,
  4456. 'product': bulkdefProduct,
  4457. 'prefix': bulkdefPrefix}
  4458. listView = None
  4459. # direct link to a specific table?
  4460. if action:
  4461. if bulkdef.has_key(action):
  4462. help = bulkdef[action].syntax
  4463. form.editboxes[0].fields['table'][0].value = action
  4464. form.editboxes[0].fields['textarea'][0].value = help
  4465. # form submitted?
  4466. if req.form.has_key(form.cnameConfirm) and len(req.form['table']):
  4467. # Get data from uploaded file or from textarea
  4468. fileinput = req.form['file']
  4469. input = req.form['textarea']
  4470. if len(fileinput):
  4471. input = fileinput
  4472. # Try decoding different encodings
  4473. for encoding in BULK_TRY_ENCODINGS:
  4474. try:
  4475. # Work internally with DEFAULT_ENCODING (utf-8)
  4476. input = input.decode(encoding).encode(DEFAULT_ENCODING)
  4477. break
  4478. except UnicodeError:
  4479. pass
  4480. except:
  4481. raise(repr(encoding))
  4482. input = input.split('\n')
  4483. # strip cr
  4484. i = []
  4485. for line in input:
  4486. i.append(line.strip('\r'))
  4487. input = i
  4488. separator = req.form['separator']
  4489. table = req.form['table']
  4490. parsed = bulkImportParse(input,bulkdef[table],separator)
  4491. rows = []
  4492. for p in parsed:
  4493. status,data,remark,line,linenr = p
  4494. if status == BULK_STATUS_OK:
  4495. row = [(['<IMG src="' + BULK_IMG_GREEN + '">'],False),
  4496. ([linenr],False),
  4497. ([line],False),
  4498. ([remark],False)]
  4499. elif status == BULK_STATUS_YELLOW_ERROR:
  4500. row = [(['<IMG src="' + BULK_IMG_YELLOW + '">'],False),
  4501. ([linenr],False),
  4502. ([line],False),
  4503. ([remark],False)]
  4504. elif status == BULK_STATUS_RED_ERROR:
  4505. row = [(['<IMG src="' + BULK_IMG_RED + '">'],False),
  4506. ([linenr],False),
  4507. ([line],False),
  4508. ([remark],False)]
  4509. rows.append((data,row))
  4510. # show list
  4511. list = selectList()
  4512. list.action = BASEPATH + 'bulk/'
  4513. list.isBulkList = True
  4514. list.imgGreen = BULK_IMG_GREEN
  4515. list.imgYellow = BULK_IMG_YELLOW
  4516. list.imgRed = BULK_IMG_RED
  4517. list.legendGreen = 'No errors. Row will be imported.'
  4518. list.legendYellow = 'Other error. Row will not be imported.'
  4519. list.legendRed = 'Syntax error. Row will not be imported.'
  4520. list.title = 'Preview import'
  4521. list.hiddenData = []
  4522. list.hiddenData.append((BULK_TABLENAME,req.form['table']))
  4523. list.hiddenData.append((BULK_SEPARATOR,req.form['separator']))
  4524. for p in parsed:
  4525. status,data,remark,line,linenr = p
  4526. if status == BULK_STATUS_OK:
  4527. list.hiddenData.append((BULK_HIDDEN_DATA,line))
  4528. list.headings = ['','Line','Input','Remark']
  4529. list.rows = rows
  4530. form = None
  4531. elif req.form.has_key(selectList.cnameBulkConfirm):
  4532. # import confirmed after preview
  4533. table = req.form[BULK_TABLENAME]
  4534. separator = req.form[BULK_SEPARATOR]
  4535. form.status = seeddbStatus()
  4536. if req.form.has_key(BULK_HIDDEN_DATA):
  4537. data = req.form[BULK_HIDDEN_DATA]
  4538. result = bulkInsert(data,bulkdef[table],separator)
  4539. noun = ' rows'
  4540. if result == 1:
  4541. noun = ' row'
  4542. form.status.messages.append('Inserted ' + str(result) + noun)
  4543. page = pageList[table]
  4544. listView = page.listDef(req,page,None)
  4545. listView.status = form.status
  4546. listView.fill(req)
  4547. form = None
  4548. else:
  4549. form.status.errors.append('No rows to insert.')
  4550. nameSpace = {'entryList': listView, 'editList': list, 'editForm': form}
  4551. template = seeddbTemplate(searchList=[nameSpace])
  4552. template.path = EDITPATH + [('Bulk import',False)]
  4553. return template.respond()
  4554. #Function for bulk inserting
  4555. def bulkInsert(data,bulkdef,separator):
  4556. """ Inserts data when bulk importing. """
  4557. if not type(data) is list:
  4558. data = [data]
  4559. prerowlist = []
  4560. for line in data:
  4561. fields = line.split(separator)
  4562. row = {}
  4563. inputLen = len(fields)
  4564. # If we've got additional arguments (service or netbox)
  4565. # make sure we don't exceed number of arguments specified
  4566. # by bulkdef.fields
  4567. if inputLen > len(bulkdef.fields):
  4568. inputLen = len(bulkdef.fields)
  4569. for i in range(0,inputLen):
  4570. # fieldname,maxlen,required,use
  4571. field,ml,req,use = bulkdef.fields[i]
  4572. row[field] = fields[i]
  4573. # Add extra arguments
  4574. excessCount = 0
  4575. i = inputLen
  4576. if len(fields) > len(bulkdef.fields):
  4577. while(i < (len(fields))):
  4578. field = BULK_UNSPECIFIED_FIELDNAME + str(excessCount)
  4579. row[field] = fields[i]
  4580. excessCount += 1
  4581. i += 1
  4582. prerowlist.append(row)
  4583. # Do table specific things with the data before insterting
  4584. # (create missing devices for netboxes for example)
  4585. if bulkdef.process:
  4586. rowlist = []
  4587. for row in prerowlist:
  4588. row = bulkdef.preInsert(row)
  4589. if row:
  4590. rowlist.append(row)
  4591. else:
  4592. # do nothing, just insert it
  4593. rowlist = prerowlist
  4594. # Remove all fields
  4595. # where use = False
  4596. if not bulkdef.onlyProcess:
  4597. for row in rowlist:
  4598. for i in range(0,len(fields)):
  4599. # fieldname,maxlen,required,use
  4600. field,ml,req,use = bulkdef.fields[i]
  4601. # Escape special characters
  4602. if row.has_key(field):
  4603. row[field] = row[field].replace("'","\\'")
  4604. #row[field] = row[field].replace('"','\\"')
  4605. if not use:
  4606. if row.has_key(field):
  4607. del(row[field])
  4608. addEntryBulk(rowlist,bulkdef.tablename)
  4609. return len(rowlist)
  4610. # Classes describing the fields for bulk import
  4611. class bulkdefCabling:
  4612. """ Contains defintion of fields for bulk importing cabling """
  4613. tablename = 'cabling'
  4614. table = nav.db.manage.Cabling
  4615. uniqueField = ['roomid','jack']
  4616. enforce_max_fields = True
  4617. max_num_fields = 6
  4618. min_num_fields = 4
  4619. process = True
  4620. onlyProcess = False
  4621. syntax = '#roomid:jack:building:targetroom:[category:descr]\n'
  4622. postCheck = False
  4623. # list of (fieldname,max length,not null,use field)
  4624. fields = [('roomid',30,True,True),
  4625. ('jack',0,True,True),
  4626. ('building',0,True,True),
  4627. ('targetroom',0,True,True),
  4628. ('category',0,False,True),
  4629. ('descr',0,False,True)]
  4630. def checkValidity(cls,field,data):
  4631. status = BULK_STATUS_OK
  4632. remark = None
  4633. # locationid must exist in Location
  4634. if field == 'roomid':
  4635. if data:
  4636. if len(data):
  4637. try:
  4638. nav.db.manage.Room(data).load()
  4639. except forgetSQL.NotFound:
  4640. status = BULK_STATUS_RED_ERROR
  4641. remark = "Room '" + data + "' not found in database"
  4642. if field == 'category':
  4643. if data:
  4644. if len(data):
  4645. catlist = []
  4646. for cat in CATEGORY_LIST:
  4647. catlist.append(cat[0])
  4648. if not data in catlist:
  4649. status = BULK_STATUS_RED_ERROR
  4650. remark = "Category '" + data + "' not found in " +\
  4651. "config file"
  4652. return (status,remark)
  4653. checkValidity = classmethod(checkValidity)
  4654. def preInsert(cls,row):
  4655. """ Inserts default value for category. """
  4656. if not row.has_key('category'):
  4657. # Set default category
  4658. row['category'] = CATEGORY_LIST[0][0]
  4659. else:
  4660. if not len(row['category']):
  4661. # Set default category
  4662. row['category'] = CATEGORY_LIST[0][0]
  4663. # Check uniqueness
  4664. where = ""
  4665. first = True
  4666. for field in cls.uniqueField:
  4667. if not first:
  4668. where += 'AND '
  4669. where += field + "='" + row[field] + "' "
  4670. first = False
  4671. result = cls.table.getAll(where)
  4672. if result:
  4673. row = None
  4674. return row
  4675. preInsert = classmethod(preInsert)
  4676. class bulkdefPatch:
  4677. """ Contains defintion of fields for bulk importing patches """
  4678. tablename = 'patch'
  4679. table = nav.db.manage.Patch
  4680. uniqueField = ['swportid','cablingid']
  4681. enforce_max_fields = True
  4682. max_num_fields = 6
  4683. min_num_fields = 5
  4684. process = True
  4685. onlyProcess = False
  4686. syntax = '#switch(sysname):module:port:roomid:jack[:split]\n'
  4687. postCheck = False
  4688. # list of (fieldname,max length,not null,use field)
  4689. fields = [('sysname',0,True,False),
  4690. ('module',0,True,False),
  4691. ('port',0,True,False),
  4692. ('roomid',0,True,False),
  4693. ('jack',0,True,False),
  4694. ('split',0,False,True)]
  4695. def checkValidity(cls,field,data):
  4696. """ Checks validity (eg. existance) of input fields."""
  4697. status = BULK_STATUS_OK
  4698. remark = None
  4699. if field == 'roomid':
  4700. if data:
  4701. if len(data):
  4702. try:
  4703. room = nav.db.manage.Room(data)
  4704. cls.roomId = room.roomid
  4705. # If room existsm, check if netbox is in it
  4706. where = "netboxid=" + str(cls.netboxId) + " AND " +\
  4707. "roomid='" + cls.roomId + "'"
  4708. box = nav.db.manage.Netbox.getAll(where)
  4709. if not box:
  4710. sw = nav.db.manage.Netbox(cls.netboxId).sysname
  4711. status = BULK_STATUS_RED_ERROR
  4712. remark = "Switch '" + sw + "' not in room " +\
  4713. cls.roomId
  4714. except forgetSQL.NotFound:
  4715. status = BULK_STATUS_RED_ERROR
  4716. remark = "Room '" + data + "' not found in database"
  4717. if field == 'sysname':
  4718. if data:
  4719. if len(data):
  4720. # Check if switch is given as ip or sysname
  4721. validIP = nav.util.isValidIP(data)
  4722. ip = None
  4723. if validIP:
  4724. # This is an IP
  4725. ip = validIP
  4726. else:
  4727. # Not an IP, possibly a hostname
  4728. try:
  4729. ip = gethostbyname(data)
  4730. sysname = data
  4731. except:
  4732. status = BULK_STATUS_RED_ERROR
  4733. remark = "Switch '" + data + "' not found in database"
  4734. # 'ip' should be numerical ip now,
  4735. # else there already was an error
  4736. if ip:
  4737. where = "ip='" + ip + "'"
  4738. box = nav.db.manage.Netbox.getAll(where)
  4739. if box:
  4740. box = box[0]
  4741. cls.netboxId = box.netboxid
  4742. else:
  4743. status = BULK_STATUS_RED_ERROR
  4744. remark = "Switch '" + data + "' not found in database"
  4745. if field == 'module':
  4746. if data:
  4747. if len(data):
  4748. try:
  4749. tst = int(data)
  4750. where = "module='" + data + "' AND " +\
  4751. "netboxid='" + str(cls.netboxId) + "'"
  4752. module = nav.db.manage.Module.getAll(where)
  4753. if module:
  4754. module = module[0]
  4755. cls.moduleId = module.moduleid
  4756. else:
  4757. status = BULK_STATUS_RED_ERROR
  4758. sw = nav.db.manage.Netbox(cls.netboxId).sysname
  4759. remark = "Module '" + data + "' in switch " +\
  4760. sw + " not found in database"
  4761. except ValueError:
  4762. status = BULK_STATUS_RED_ERROR
  4763. remark = "Module must be integer"
  4764. if field == 'port':
  4765. # Check if this port on the specified module in the switch
  4766. # is present in the database
  4767. if data:
  4768. if len(data):
  4769. try:
  4770. tst = int(data)
  4771. where = "moduleid='" + str(cls.moduleId) + "' AND " +\
  4772. "port='" + data + "'"
  4773. swport = nav.db.manage.Swport.getAll(where)
  4774. if swport:
  4775. swport = swport[0]
  4776. cls.swportId = swport.swportid
  4777. else:
  4778. status = BULK_STATUS_RED_ERROR
  4779. sw = nav.db.manage.Netbox(cls.netboxId).sysname
  4780. module = nav.db.manage.Module(cls.moduleId).module
  4781. remark = "Port '" + data + "', module '" +\
  4782. str(module) + "' in switch " +\
  4783. sw + " not found in database"
  4784. except ValueError:
  4785. status = BULK_STATUS_RED_ERROR
  4786. remark = "Port must be integer"
  4787. if field == 'jack':
  4788. if data:
  4789. if len(data):
  4790. where = "roomid='" + cls.roomId + "' AND " +\
  4791. "jack='" + data + "'"
  4792. cabling = nav.db.manage.Cabling.getAll(where)
  4793. if cabling:
  4794. cabling = cabling[0]
  4795. cls.cablingId = cabling.cablingid
  4796. else:
  4797. status = BULK_STATUS_RED_ERROR
  4798. remark = "Cabling between " +\
  4799. " jack '" + data + "' " +\
  4800. " and room '" + cls.roomId + "'" +\
  4801. " not found in database"
  4802. if field == 'split':
  4803. if data:
  4804. if len(data):
  4805. splitlist = []
  4806. for split in SPLIT_LIST:
  4807. splitlist.append(split[0])
  4808. if not data in splitlist:
  4809. status = BULK_STATUS_RED_ERROR
  4810. remark = "Split '" + data + "' not found in config " +\
  4811. "file"
  4812. return (status,remark)
  4813. checkValidity = classmethod(checkValidity)
  4814. def preInsert(cls,row):
  4815. """ Gets required data from db before inserting row. """
  4816. # Check if sysname is given as ip or sysname
  4817. validIP = nav.util.isValidIP(row['sysname'])
  4818. ip = None
  4819. if validIP:
  4820. # This is an IP
  4821. ip = validIP
  4822. else:
  4823. # Not an IP, possibly a hostname
  4824. try:
  4825. ip = gethostbyname(row['sysname'])
  4826. except:
  4827. # DNS lookup failed
  4828. # could happen if DNS stopped working between
  4829. # preview and insert
  4830. row = None
  4831. if ip:
  4832. where = "ip='" + ip + "'"
  4833. box = nav.db.manage.Netbox.getAll(where)
  4834. box = box[0]
  4835. where = "module='" + row['module'] + "' AND " +\
  4836. "netboxid='" + str(box.netboxid) + "'"
  4837. module = nav.db.manage.Module.getAll(where)
  4838. module = module[0]
  4839. where = "moduleid='" + str(module.moduleid) + "' AND " +\
  4840. "port='" + row['port'] + "'"
  4841. swport = nav.db.manage.Swport.getAll(where)
  4842. swport = swport[0]
  4843. where = "roomid='" + row['roomid'] + "' AND " +\
  4844. "jack='" + row['jack'] + "'"
  4845. cabling = nav.db.manage.Cabling.getAll(where)
  4846. cabling = cabling[0]
  4847. row['swportid'] = str(swport.swportid)
  4848. row['cablingid'] = str(cabling.cablingid)
  4849. if not row.has_key('split'):
  4850. # Set default split
  4851. row['split'] = SPLIT_LIST[0][0]
  4852. if not len(row['split']):
  4853. row['split'] = SPLIT_LIST[0][0]
  4854. # Check if the selected jack already belongs to a patch
  4855. where = "cablingid='" + row['cablingid'] + "'"
  4856. otherPatch = nav.db.manage.Patch.getAll(where)
  4857. if otherPatch:
  4858. # Already exists a patch with this jack, it must
  4859. # be splitted, if split is changed then do something
  4860. otherPatch = otherPatch[0]
  4861. otherSplit = otherPatch.split
  4862. if SPLIT_OPPOSITE[row['split']] != otherSplit:
  4863. # Splits are different, either update split on the
  4864. # other entry, or delete it if this split='no'
  4865. otherPatchId = str(otherPatch.patchid)
  4866. # SPLIT_LIST[0][0] is default entry id
  4867. if row['split'] == SPLIT_LIST[0][0]:
  4868. # Delete other entry
  4869. deleteEntry([otherPatchId],'patch','patchid')
  4870. else:
  4871. # Update other entry
  4872. fields = {'split': SPLIT_OPPOSITE[row['split']]}
  4873. updateEntryFields(fields,'patch',
  4874. 'patchid',otherPatchId)
  4875. # Check uniqueness
  4876. where = ""
  4877. first = True
  4878. for field in cls.uniqueField:
  4879. if not first:
  4880. where += 'AND '
  4881. where += field + "='" + row[field] + "' "
  4882. first = False
  4883. result = cls.table.getAll(where)
  4884. if result:
  4885. row = None
  4886. return row
  4887. preInsert = classmethod(preInsert)
  4888. class bulkdefLocation:
  4889. """ Contains defintion of fields for bulk importing locations """
  4890. # number of fields
  4891. tablename = 'location'
  4892. table = manage.Location
  4893. uniqueField = 'locationid'
  4894. enforce_max_fields = True
  4895. max_num_fields = 2
  4896. min_num_fields = 2
  4897. process = False
  4898. onlyProcess = False
  4899. syntax = '#locationid:descr\n'
  4900. postCheck = False
  4901. # list of (fieldname,max length,not null,use field)
  4902. fields = [('locationid',30,True,True),
  4903. ('descr',0,True,True)]
  4904. def checkValidity(cls,field,data):
  4905. """ Checks validity of fields. (No need for location). """
  4906. status = True
  4907. remark = None
  4908. return (status,remark)
  4909. checkValidity = classmethod(checkValidity)
  4910. class bulkdefRoom:
  4911. """ Contains defintion of fields for bulk importing rooms """
  4912. # number of fields
  4913. tablename = 'room'
  4914. table = manage.Room
  4915. uniqueField = 'roomid'
  4916. enforce_max_fields = True
  4917. max_num_fields = 7
  4918. min_num_fields = 1
  4919. process = False
  4920. onlyProcess = False
  4921. syntax = '#roomid[:locationid:descr:opt1:opt2:opt3:opt4]\n'
  4922. postCheck = False
  4923. # list of (fieldname,max length,not null,use field)
  4924. fields = [('roomid',30,True,True),
  4925. ('locationid',30,False,True),
  4926. ('descr',0,False,True),
  4927. ('opt1',0,False,True),
  4928. ('opt2',0,False,True),
  4929. ('opt3',0,False,True),
  4930. ('opt4',0,False,True)]
  4931. def checkValidity(cls,field,data):
  4932. status = BULK_STATUS_OK
  4933. remark = None
  4934. # locationid must exist in Location
  4935. if field == 'locationid':
  4936. if data:
  4937. if len(data):
  4938. try:
  4939. manage.Location(data).load()
  4940. except forgetSQL.NotFound:
  4941. status = BULK_STATUS_RED_ERROR
  4942. remark = "Location '" + data + "' not found in database"
  4943. return (status,remark)
  4944. checkValidity = classmethod(checkValidity)
  4945. class bulkdefOrg:
  4946. """ Contains field definitions for bulk importing orgs. """
  4947. tablename = 'org'
  4948. table = manage.Org
  4949. uniqueField = 'orgid'
  4950. enforce_max_fields = True
  4951. max_num_fields = 6
  4952. min_num_fields = 1
  4953. process = False
  4954. onlyProcess = False
  4955. syntax = '#orgid[:parent:description:optional1:optional2:optional3]\n'
  4956. postCheck = False
  4957. # list of (fieldname,max length,not null,use field)
  4958. fields = [('orgid',30,True,True),
  4959. ('parent',30,False,True),
  4960. ('descr',0,False,True),
  4961. ('opt1',0,False,True),
  4962. ('opt2',0,False,True),
  4963. ('opt3',0,False,True)]
  4964. def checkValidity(cls,field,data):
  4965. """ Checks validity of input fields. """
  4966. status = BULK_STATUS_OK
  4967. remark = None
  4968. if field == 'parent' and len(data):
  4969. try:
  4970. manage.Org(data).load()
  4971. except forgetSQL.NotFound:
  4972. status = BULK_STATUS_RED_ERROR
  4973. remark = "Parent '" + data + "' not found in database"
  4974. return (status,remark)
  4975. checkValidity = classmethod(checkValidity)
  4976. class bulkdefUsage:
  4977. """ Contains field definitions for bulk importing usage. """
  4978. # number of fields
  4979. tablename = 'usage'
  4980. table = manage.Usage
  4981. uniqueField = 'usageid'
  4982. enforce_max_fields = True
  4983. max_num_fields = 2
  4984. min_num_fields = 2
  4985. process = False
  4986. onlyProcess = False
  4987. syntax = '#usageid:descr\n'
  4988. postCheck = False
  4989. # list of (fieldname,max length,not null,use field)
  4990. fields = [('usageid',30,True,True),
  4991. ('descr',0,True,True)]
  4992. def checkValidity(cls,field,data):
  4993. """ Checks validity of input fields. """
  4994. status = BULK_STATUS_OK
  4995. remark = None
  4996. return (status,remark)
  4997. checkValidity = classmethod(checkValidity)
  4998. class bulkdefVendor:
  4999. """ Contains field information for bulk importing vendors. """
  5000. # number of fields
  5001. tablename = 'vendor'
  5002. table = manage.Vendor
  5003. uniqueField = 'vendorid'
  5004. enforce_max_fields = True
  5005. max_num_fields = 1
  5006. min_num_fields = 1
  5007. process = False
  5008. onlyProcess = False
  5009. syntax = '#vendorid\n'
  5010. postCheck = False
  5011. # list of (fieldname,max length,not null,use field)
  5012. fields = [('vendorid',15,True,True)]
  5013. def checkValidity(cls,field,data):
  5014. """ Checks validity of input fields. """
  5015. status = BULK_STATUS_OK
  5016. remark = None
  5017. return (status,remark)
  5018. checkValidity = classmethod(checkValidity)
  5019. class bulkdefSubcat:
  5020. """ Contains field information for bulk importing subcats. """
  5021. tablename = 'subcat'
  5022. table = manage.Subcat
  5023. uniqueField = 'subcatid'
  5024. enforce_max_fields = True
  5025. max_num_fields = 3
  5026. min_num_fields = 3
  5027. process = False
  5028. onlyProcess = False
  5029. syntax = '#subcatid:catid:description\n'
  5030. postCheck = False
  5031. # list of (fieldname,max length,not null,use field)
  5032. fields = [('subcatid',0,True,True),
  5033. ('catid',8,True,True),
  5034. ('descr',0,True,True)]
  5035. def checkValidity(cls,field,data):
  5036. """ Checks validity of input fields. """
  5037. status = BULK_STATUS_OK
  5038. remark = None
  5039. if field == 'catid':
  5040. try:
  5041. manage.Cat(data).load()
  5042. except forgetSQL.NotFound:
  5043. status = BULK_STATUS_RED_ERROR
  5044. remark = "Category '" + data + "' not found in database"
  5045. return (status,remark)
  5046. checkValidity = classmethod(checkValidity)
  5047. class bulkdefType:
  5048. """ Contains field defintions for bulk importing types. """
  5049. # number of fields
  5050. tablename = 'type'
  5051. table = manage.Type
  5052. uniqueField = 'typename'
  5053. enforce_max_fields = True
  5054. max_num_fields = 7
  5055. min_num_fields = 3
  5056. process = True
  5057. onlyProcess = False
  5058. syntax = '#vendorid:typename:sysoid[:description:frequency:cdp=(yes|no)' +\
  5059. ':tftp=(yes|no)]\n'
  5060. postCheck = False
  5061. # list of (fieldname,max length,not null,use field)
  5062. fields = [('vendorid',15,True,True),
  5063. ('typename',0,True,True),
  5064. ('sysobjectid',0,True,True),
  5065. ('descr',0,False,True),
  5066. ('frequency',0,False,True),
  5067. ('cdp',0,False,True),
  5068. ('tftp',0,False,True)]
  5069. def checkValidity(cls,field,data):
  5070. """ Checks validity of input fields. """
  5071. status = BULK_STATUS_OK
  5072. remark = None
  5073. if field == 'vendorid':
  5074. try:
  5075. manage.Vendor(data).load()
  5076. except forgetSQL.NotFound:
  5077. status = BULK_STATUS_RED_ERROR
  5078. remark = "Vendor '" + data + "' not found in database"
  5079. return (status,remark)
  5080. checkValidity = classmethod(checkValidity)
  5081. def preInsert(cls,row):
  5082. """ Alter fields before inserting. (set correct value for cdp
  5083. and tftp if anything is input in those fields) """
  5084. if row.has_key('cdp'):
  5085. if len(row['cdp']):
  5086. if not row['cdp'].lower() == 'no':
  5087. row['cdp'] = '1'
  5088. else:
  5089. row['cdp'] = '0'
  5090. if row.has_key('tftp'):
  5091. if len(row['tftp']):
  5092. if not row['tftp'].lower() == 'no':
  5093. row['tftp'] = '1'
  5094. else:
  5095. row['tftp'] = '0'
  5096. return row
  5097. preInsert = classmethod(preInsert)
  5098. class bulkdefProduct:
  5099. """ Contains field definitions for bulk importing products. """
  5100. # number of fields
  5101. tablename = 'product'
  5102. table = manage.Product
  5103. uniqueField = 'productno'
  5104. enforce_max_fields = True
  5105. max_num_fields = 3
  5106. min_num_fields = 2
  5107. process = True
  5108. onlyProcess = False
  5109. syntax = '#vendorid:productno[:description]\n'
  5110. postCheck = False
  5111. # list of (fieldname,max length,not null,use field)
  5112. fields = [('vendorid',15,True,True),
  5113. ('productno',0,True,True),
  5114. ('descr',0,False,True)]
  5115. def checkValidity(cls,field,data):
  5116. """ Checks validity of input fields. """
  5117. status = BULK_STATUS_OK
  5118. remark = None
  5119. if field == 'vendorid':
  5120. try:
  5121. manage.Vendor(data).load()
  5122. except forgetSQL.NotFound:
  5123. status = BULK_STATUS_RED_ERROR
  5124. remark = "Vendor '" + data + "' not found in database"
  5125. return (status,remark)
  5126. checkValidity = classmethod(checkValidity)
  5127. def preInsert(cls,row):
  5128. """ Alter fields before inserting. (set correct value for cdp
  5129. and tftp if anything is input in those fields) """
  5130. if row.has_key('cdp'):
  5131. row['cdp'] = '1'
  5132. if row.has_key('tftp'):
  5133. row['tftp'] = '1'
  5134. return row
  5135. preInsert = classmethod(preInsert)
  5136. class bulkdefNetbox:
  5137. """ Contains field definitions for bulk importing boxes. """
  5138. tablename = 'netbox'
  5139. table = manage.Netbox
  5140. uniqueField = 'ip'
  5141. # number of fields
  5142. enforce_max_fields = False
  5143. max_num_fields = 8
  5144. min_num_fields = 4
  5145. process = True
  5146. onlyProcess = True
  5147. syntax = '#roomid:ip:orgid:catid:[ro:serial:rw:function:subcat1:subcat2..]\n'
  5148. # list of (fieldname,max length,not null,use field)
  5149. fields = [('roomid',0,True,True),
  5150. ('ip',0,True,True),
  5151. ('orgid',30,True,True),
  5152. ('catid',8,True,True),
  5153. ('ro',0,False,True),
  5154. ('serial',0,False,False),
  5155. ('rw',0,False,True),
  5156. ('function',0,False,False)]
  5157. def postCheck(cls,data):
  5158. """ Checks each box before inserting. Tries to connect with snmp
  5159. if ro is specified. """
  5160. status = BULK_STATUS_OK
  5161. remark = None
  5162. try:
  5163. hasRO = False
  5164. if data.has_key('ro'):
  5165. if len(data['ro']):
  5166. hasRO = True
  5167. hasSerial = False
  5168. if data.has_key('serial'):
  5169. if len(data['serial']):
  5170. hasSerial = True
  5171. if (not hasRO) and manage.Cat(data['catid']).req_snmp:
  5172. status = BULK_STATUS_YELLOW_ERROR
  5173. raise("This category requires an RO community")
  5174. ## SERIAL IS NOW OPTIONAL
  5175. #if not (hasRO or hasSerial):
  5176. # status = BULK_STATUS_RED_ERROR
  5177. # raise("Neither RO, nor serial specified.")
  5178. if hasRO:
  5179. error = False
  5180. try:
  5181. box = initBox.Box(data['ip'],data['ro'])
  5182. # DeviceId / Serial takes too long time to get
  5183. # We will not do it here
  5184. # box.getDeviceId()
  5185. # if (not hasSerial) and (not box.serial):
  5186. # status = BULK_STATUS_YELLOW_ERROR
  5187. # error = "No serial returned by SNMP, and no serial given."
  5188. if (not box.typeid):
  5189. if manage.Cat(data['catid']).req_snmp:
  5190. status = BULK_STATUS_YELLOW_ERROR
  5191. error = "Got SNMP response, but couldn't get type which is required for IP devices of this category. Add type manually."
  5192. else:
  5193. status = BULK_STATUS_OK
  5194. error = "Got SNMP response, but couldn't get type (type isn't required for this category)."
  5195. except nav.Snmp.TimeOutException:
  5196. if manage.Cat(data['catid']).req_snmp:
  5197. # Snmp failed, but is required by this CAT
  5198. status = BULK_STATUS_YELLOW_ERROR
  5199. raise("RO given, but failed to contact IP device by SNMP (IP devices of this category are required to answer).")
  5200. else:
  5201. # Snmp failed, but isn't required by this CAT
  5202. if hasSerial:
  5203. status = BULK_STATUS_OK
  5204. raise("RO given, but failed to contact IP device by SNMP (IP devices of this cateogry aren't required to answer as long as a serial is given).")
  5205. else:
  5206. status = BULK_STATUS_YELLOW_ERROR
  5207. raise("RO given, but failed to contact IP device by SNMP (IP devices of this cateogry aren't required to answer, but you must supply a serial if they don't).")
  5208. except Exception, e:
  5209. status = BULK_STATUS_RED_ERROR
  5210. error = 'Uknown error while querying IP device: '
  5211. error += str(sys.exc_info()[0]) + ': '
  5212. error += str(sys.exc_info()[1])
  5213. if error:
  5214. raise(error)
  5215. except:
  5216. remark = sys.exc_info()[0]
  5217. return (status,remark,data)
  5218. postCheck = classmethod(postCheck)
  5219. def checkValidity(cls,field,data):
  5220. """ Checks the validity of the input fields. """
  5221. status = BULK_STATUS_OK
  5222. remark = None
  5223. if field == 'ip':
  5224. if not nav.util.isValidIP(data):
  5225. remark = "Invalid IP address"
  5226. status = BULK_STATUS_RED_ERROR
  5227. if field == 'roomid':
  5228. try:
  5229. manage.Room(data).load()
  5230. except forgetSQL.NotFound:
  5231. status = BULK_STATUS_RED_ERROR
  5232. remark = "Room '" + data + "' not found in database"
  5233. if field == 'orgid':
  5234. try:
  5235. manage.Org(data).load()
  5236. except forgetSQL.NotFound:
  5237. status = BULK_STATUS_RED_ERROR
  5238. remark = "Organisation '" + data + "' not found in database"
  5239. if field == 'catid':
  5240. try:
  5241. manage.Cat(data).load()
  5242. except forgetSQL.NotFound:
  5243. status = BULK_STATUS_RED_ERROR
  5244. remark = "Invalid category '" + data + "'"
  5245. if field == 'serial':
  5246. if len(data):
  5247. where = "serial='%s'" % (data,)
  5248. device = manage.Device.getAll(where)
  5249. if device:
  5250. # There exists a device with this serial,
  5251. # must check if any netbox is connected with this
  5252. # device
  5253. where = "deviceid='%s'" % (device[0].deviceid,)
  5254. netbox = manage.Netbox.getAll(where)
  5255. if netbox:
  5256. status = BULK_STATUS_RED_ERROR
  5257. remark = "An IP device with the serial '" + data + \
  5258. "' already exists"
  5259. if field == BULK_UNSPECIFIED_FIELDNAME:
  5260. # These are subcats
  5261. # Need to check not only if the subcat exists, but
  5262. # also if the cat is correct
  5263. try:
  5264. manage.Subcat(data).load()
  5265. except forgetSQL.NotFound:
  5266. status = BULK_STATUS_RED_ERROR
  5267. remark = "Invalid subcat '" + data + "'"
  5268. return (status,remark)
  5269. checkValidity = classmethod(checkValidity)
  5270. def preInsert(cls,row):
  5271. """ Changes rows before inserting. Gets required data from db. """
  5272. # Get sysname
  5273. try:
  5274. sysname = gethostbyaddr(row['ip'])[0]
  5275. except:
  5276. sysname = row['ip']
  5277. row['sysname'] = sysname
  5278. # Get prefixid
  5279. query = "SELECT prefixid FROM prefix WHERE '%s'::inet << netaddr" \
  5280. % (row['ip'],)
  5281. try:
  5282. result = executeSQLreturn(query)
  5283. row['prefixid'] = str(result[0][0])
  5284. except:
  5285. pass
  5286. deviceid = None
  5287. box = None
  5288. if row.has_key('ro'):
  5289. if len(row['ro']):
  5290. try:
  5291. box = initBox.Box(row['ip'],row['ro'])
  5292. if box.typeid:
  5293. typeId = str(box.typeid)
  5294. row['typeid'] = typeId
  5295. if box.snmpversion:
  5296. row['snmp_version'] = str(box.snmpversion[0])
  5297. # getDeviceId() now returns an Int
  5298. deviceid = box.getDeviceId()
  5299. except:
  5300. # If initBox fails, always make a new device
  5301. deviceid = None
  5302. if deviceid:
  5303. # Already got a device from initbox
  5304. row['deviceid'] = str(deviceid)
  5305. if row.has_key('serial'):
  5306. # Serial shouldn't be inserted into Netbox table
  5307. # remove it from the row
  5308. del(row['serial'])
  5309. else:
  5310. # Must make a new device
  5311. newSerial = None
  5312. # Got serial from row?
  5313. if row.has_key('serial'):
  5314. if len(row['serial']):
  5315. newSerial = row['serial']
  5316. # Serial shouldn't be inserted into Netbox table
  5317. # remove it from the row
  5318. del(row['serial'])
  5319. # If we got a serial by SNMP and none was specified in the bulk
  5320. # data, use the one retrieved by SNMP
  5321. if box and not newSerial:
  5322. if box.serial:
  5323. newSerial = str(box.serial)
  5324. fields = {}
  5325. if newSerial:
  5326. # Serial given in row, or retrieved by snmp
  5327. fields = {'serial': newSerial}
  5328. # Must check if a device with this serial is already present
  5329. where = "serial='%s'" % (fields['serial'])
  5330. device = manage.Device.getAll(where)
  5331. if device:
  5332. # Found device, and it is unique (serial must be unique)
  5333. deviceid = str(device[0].deviceid)
  5334. if not deviceid:
  5335. # Make new device
  5336. deviceid = addEntryFields(fields,
  5337. 'device',
  5338. ('deviceid','device_deviceid_seq'))
  5339. row['deviceid'] = deviceid
  5340. if row:
  5341. # Function
  5342. netboxFunction = None
  5343. if row.has_key('function'):
  5344. if len(row['function']):
  5345. netboxFunction = row['function']
  5346. del(row['function'])
  5347. # Subcat's
  5348. excessCount = 0
  5349. excessField = BULK_UNSPECIFIED_FIELDNAME + str(excessCount)
  5350. subcatList = []
  5351. while(row.has_key(excessField)):
  5352. subcatList.append(row[excessField])
  5353. del(row[excessField])
  5354. excessCount += 1
  5355. excessField = BULK_UNSPECIFIED_FIELDNAME + str(excessCount)
  5356. # Insert netbox
  5357. netboxId = addEntryFields(row,
  5358. 'netbox',
  5359. ('netboxid','netbox_netboxid_seq'))
  5360. # Insert netboxfunction
  5361. if netboxFunction:
  5362. fields = {'netboxid': netboxId,
  5363. 'var': 'function',
  5364. 'val': netboxFunction}
  5365. addEntryFields(fields,
  5366. 'netboxinfo')
  5367. if subcatList:
  5368. for subcat in subcatList:
  5369. if len(subcat):
  5370. sql = "SELECT subcatid FROM subcat WHERE " +\
  5371. "subcatid='%s'" % (subcat,)
  5372. result = executeSQLreturn(sql)
  5373. if result:
  5374. fields = {'netboxid': netboxId,
  5375. 'category': subcat}
  5376. addEntryFields(fields,
  5377. 'netboxcategory')
  5378. return row
  5379. preInsert = classmethod(preInsert)
  5380. class bulkdefService:
  5381. """ Contains field definitions for bulk importing services. """
  5382. tablename = 'service'
  5383. table = manage.Service
  5384. uniqueField = None
  5385. enforce_max_fields = False
  5386. max_num_fields = 2
  5387. min_num_fields = 2
  5388. process = True
  5389. onlyProcess = True
  5390. syntax = '#ip/sysname:handler[:arg=value[:arg=value]]\n'
  5391. postCheck = False
  5392. # Seperator for optargs arguments
  5393. optSep = '='
  5394. # list of (fieldname,max length,not null,use field)
  5395. fields = [('netboxid',0,True,True),
  5396. ('handler',0,True,True)]
  5397. def insert(cls,row):
  5398. raise(repr(row))
  5399. insert = classmethod(insert)
  5400. def postCheck(cls,data):
  5401. """ Changes row data before inserting. Does DNS lookup etc. """
  5402. status = BULK_STATUS_OK
  5403. remark = None
  5404. try:
  5405. # Check sysname/ip
  5406. try:
  5407. ip = gethostbyname(data['netboxid'])
  5408. except gaierror:
  5409. raise ("DNS query for '%s' failed." % (data['netboxid'],))
  5410. where = "ip='%s'" % (ip,)
  5411. box = manage.Netbox.getAll(where)
  5412. if box:
  5413. data['netboxid'] = str(box[0].netboxid)
  5414. else:
  5415. raise("No box with sysname or ip '" + data['netboxid'] + \
  5416. "' found in database.")
  5417. # Check validity of additional arguments
  5418. properties = getDescription(data['handler'])
  5419. optargs = []
  5420. args = []
  5421. if properties:
  5422. if properties.has_key('optargs'):
  5423. for optarg in properties['optargs']:
  5424. optargs.append(optarg)
  5425. if properties.has_key('args'):
  5426. for arg in properties['args']:
  5427. args.append(arg)
  5428. seperator = bulkdefService.optSep
  5429. excessCount = 0
  5430. excessField = BULK_UNSPECIFIED_FIELDNAME + str(excessCount)
  5431. argsData = {}
  5432. while(data.has_key(excessField)):
  5433. splitted = data[excessField].split('=',1)
  5434. if len(splitted) == 2:
  5435. if len(splitted[1]):
  5436. argsData[splitted[0]] = splitted[1]
  5437. excessCount += 1
  5438. excessField = BULK_UNSPECIFIED_FIELDNAME + str(excessCount)
  5439. for arg in args:
  5440. if not argsData.has_key(arg):
  5441. raise("Missing required argument '" + arg +"'")
  5442. del(argsData[arg])
  5443. for key in argsData.keys():
  5444. if not key in optargs:
  5445. raise("Invalid argument '" + key + "'")
  5446. except:
  5447. status = BULK_STATUS_YELLOW_ERROR
  5448. remark = sys.exc_info()[0]
  5449. return (status,remark,data)
  5450. postCheck = classmethod(postCheck)
  5451. def checkValidity(cls,field,data):
  5452. """ Checks validity of input fields. """
  5453. status = BULK_STATUS_OK
  5454. remark = None
  5455. if field == 'handler':
  5456. if not data in getCheckers():
  5457. remark = "Invalid handler '" + data + "'"
  5458. status = BULK_STATUS_RED_ERROR
  5459. return (status,remark)
  5460. checkValidity = classmethod(checkValidity)
  5461. def preInsert(cls,data):
  5462. """ Fills in all missing data before inserting entry. """
  5463. try:
  5464. ip = gethostbyname(data['netboxid'])
  5465. where = "ip='%s'" % (ip,)
  5466. box = manage.Netbox.getAll(where)
  5467. if box:
  5468. data['netboxid'] = str(box[0].netboxid)
  5469. else:
  5470. data = None
  5471. except:
  5472. data = None
  5473. if data:
  5474. fields = {'netboxid': data['netboxid'],
  5475. 'handler': data['handler']}
  5476. serviceid = addEntryFields(fields,
  5477. 'service',
  5478. ('serviceid','service_serviceid_seq'))
  5479. # Check validity of additional arguments
  5480. seperator = bulkdefService.optSep
  5481. excessCount = 0
  5482. excessField = BULK_UNSPECIFIED_FIELDNAME + str(excessCount)
  5483. argsData = {}
  5484. while(data.has_key(excessField)):
  5485. splitted = data[excessField].split('=',1)
  5486. if len(splitted) == 2:
  5487. if len(splitted[1]):
  5488. argsData[splitted[0]] = splitted[1]
  5489. excessCount += 1
  5490. excessField = BULK_UNSPECIFIED_FIELDNAME + str(excessCount)
  5491. for property,value in argsData.items():
  5492. fields = {'serviceid': serviceid,
  5493. 'property': property,
  5494. 'value': value}
  5495. addEntryFields(fields,
  5496. 'serviceproperty')
  5497. return data
  5498. preInsert = classmethod(preInsert)
  5499. class bulkdefPrefix:
  5500. """ Contains field definitions for bulk importing prefixes. """
  5501. tablename = 'prefix'
  5502. table = manage.Prefix
  5503. uniqueField = 'prefixid'
  5504. enforce_max_fields = True
  5505. max_num_fields = 7
  5506. min_num_fields = 1
  5507. process = True
  5508. onlyProcess = False
  5509. syntax= '#prefix/mask:nettype[:orgid:netident:usage:description:vlan]\n'
  5510. postCheck = False
  5511. # list of (fieldname,max length,not null,use field)
  5512. fields = [('netaddr',0,True,True),
  5513. ('nettype',0,True,False),
  5514. ('orgid',0,False,False),
  5515. ('netident',0,False,False),
  5516. ('usage',0,False,False),
  5517. ('description',0,False,False),
  5518. ('vlan',0,False,False)]
  5519. def checkValidity(cls,field,data):
  5520. """ Checks validity of fields """
  5521. status = BULK_STATUS_OK
  5522. remark = None
  5523. if field == 'netaddr':
  5524. # Valid CIDR?
  5525. try:
  5526. sql = "SELECT '%s'::inet::cidr" % (data,)
  5527. executeSQL([sql])
  5528. # Already present?
  5529. where = "netaddr='%s'" % (data,)
  5530. prefixes = manage.Prefix.getAll(where)
  5531. if prefixes:
  5532. status = BULK_STATUS_YELLOW_ERROR
  5533. remark = "CIDR already present in database."
  5534. except psycopg2.ProgrammingError:
  5535. status = BULK_STATUS_RED_ERROR
  5536. remark = "Invalid CIDR '" + data + "'"
  5537. if field == 'nettype':
  5538. # Only add nettypes we're allowed to
  5539. where = "nettypeid='%s' and edit='t'" % (data,)
  5540. result = manage.Nettype.getAll(where)
  5541. if not result:
  5542. status = BULK_STATUS_RED_ERROR
  5543. remark = "Invalid nettype '" + data + "'"
  5544. if field == 'orgid':
  5545. if data:
  5546. if len(data):
  5547. try:
  5548. manage.Org(data).load()
  5549. except forgetSQL.NotFound:
  5550. status = BULK_STATUS_RED_ERROR
  5551. remark = "Organisation '" + data + "' not found in database"
  5552. if field == 'usage':
  5553. if data:
  5554. if len(data):
  5555. try:
  5556. manage.Usage(data).load()
  5557. except forgetSQL.NotFound:
  5558. status = BULK_STATUS_RED_ERROR
  5559. remark = "Usage '" + data + "' not found in database"
  5560. return (status,remark)
  5561. checkValidity = classmethod(checkValidity)
  5562. def preInsert(cls,row):
  5563. """ Changes row data before inserting. Removes empty fields. """
  5564. fields = {}
  5565. if row.has_key('nettype'):
  5566. if len(row['nettype']):
  5567. fields['nettype'] = row['nettype']
  5568. #del(row['nettype'])
  5569. if row.has_key('orgid'):
  5570. if len(row['orgid']):
  5571. fields['orgid'] = row['orgid']
  5572. #del(row['orgid'])
  5573. if row.has_key('netident'):
  5574. if len(row['netident']):
  5575. fields['netident'] = row['netident']
  5576. #del(row['netident'])
  5577. if row.has_key('usage'):
  5578. if len(row['usage']):
  5579. fields['usageid'] = row['usage']
  5580. #del(row['usage'])
  5581. if row.has_key('description'):
  5582. if len(row['description']):
  5583. fields['description'] = row['description']
  5584. #del(row['description'])
  5585. if row.has_key('vlan'):
  5586. try:
  5587. vlan = int(row['vlan'])
  5588. fields['vlan'] = row['vlan']
  5589. except:
  5590. vlan = None
  5591. #del(row['vlan'])
  5592. vlanid = addEntryFields(fields,
  5593. 'vlan',
  5594. ('vlanid','vlan_vlanid_seq'))
  5595. row['vlanid'] = str(vlanid)
  5596. return row
  5597. preInsert = classmethod(preInsert)
  5598. # Class representing a list of entries, used by the template
  5599. class selectList:
  5600. """ Used by the template to display a list of entries. Only used by
  5601. bulk import. Could be replaced by the more flexible entryList
  5602. class for this purpose. """
  5603. # Text and controlnames for the action bar
  5604. textAdd = 'Add new'
  5605. textEdit = 'Edit selected'
  5606. textDelete = 'Delete selected'
  5607. cnameAdd = 'submit_add'
  5608. cnameEdit = 'submit_edit'
  5609. cnameDelete = 'submit_delete'
  5610. cnameChk = 'checkbox_id'
  5611. # Delete controls
  5612. cnameDeleteConfirm = 'confirm_delete'
  5613. textDeleteConfirm = 'Delete'
  5614. # Bulk controls
  5615. cnameBulkConfirm = 'confirm_bulk'
  5616. textBulkConfirm = 'Import'
  5617. # Hidden id control
  5618. cnameHiddenId = 'hidden_id'
  5619. cnameHiddenData = 'hidden_data'
  5620. # List rows where
  5621. where = None
  5622. def __init__(self):
  5623. # bulk confirm list?
  5624. self.isBulkList = False
  5625. self.hiddenIdValue = None
  5626. # is this a confirm delete list?
  5627. self.isDeleteList = False
  5628. # list of entries to delete
  5629. self.deleteList = []
  5630. # For the template
  5631. self.method = 'post'
  5632. self.action = None
  5633. self.error = None
  5634. self.status = None
  5635. self.backlink = None
  5636. # Variables that must be filled before passing to the template
  5637. self.title = None
  5638. self.headings = []
  5639. self.rows = []
  5640. # Variables used by fill()
  5641. self.table = None
  5642. self.idcol = None
  5643. self.orderBy = None
  5644. self.tablename = ''
  5645. def fill(self):
  5646. " Fill the headings and rows lists "
  5647. # fill headings
  5648. self.headings = []
  5649. if not self.isDeleteList:
  5650. self.headings = ['Select']
  5651. for heading,column,link in self.columns:
  5652. self.headings.append(heading)
  5653. # fill rows
  5654. entries = []
  5655. if not self.isDeleteList:
  5656. entries = self.table.getAllIterator(orderBy=self.orderBy,
  5657. where=self.where)
  5658. else:
  5659. for id in self.deleteList:
  5660. entries.append(self.table(id))
  5661. for entry in entries:
  5662. id = getattr(entry,self.idcol)
  5663. row = []
  5664. for heading,column,link in self.columns:
  5665. if link:
  5666. eid = id
  5667. if not type(eid) is str:
  5668. eid = str(id)
  5669. row.append(([getattr(entry,column)],BASEPATH + self.tablename + '/edit/' + eid))
  5670. else:
  5671. text = []
  5672. if type(column) is str:
  5673. text = [getattr(entry,column)]
  5674. else:
  5675. sectable,secidfield,sectextfield = column
  5676. iter = sectable.getAllIterator(where=secidfield + \
  5677. "='" + eid + "'")
  5678. for i in iter:
  5679. text.append(getattr(i,sectextfield))
  5680. row.append((text,None))
  5681. self.rows.append((id,row))