PageRenderTime 92ms CodeModel.GetById 13ms RepoModel.GetById 1ms app.codeStats 0ms

/subsystem/seeddb/src/seeddb.py

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