def clean_row(table, row, txn, schema, idl, validator_adapter): references = schema.ovs_tables[table].references children = schema.ovs_tables[table].children config_rows = schema.ovs_tables[table].config # Clean children for key in children: if key in references: kv_type = references[key].kv_type child_table = references[key].ref_table if child_table not in immutable_tables: if kv_type: row.__setattr__(key, {}) else: row.__setattr__(key, []) else: if kv_type: rowlist = row.__getattr__(key).values() else: rowlist = row.__getattr__(key) clean_subtree(child_table, rowlist, txn, schema, idl, validator_adapter) else: child_table = key clean_subtree(child_table, [], txn, schema, idl, validator_adapter, row) # Clean config fields for key in config_rows.keys(): if not config_rows[key].mutable: continue empty_val = utils.get_empty_by_basic_type(row.__getattr__(key)) row.__setattr__(key, empty_val) # Clean references for key, val in references.iteritems(): if val.relation == 'reference' and val.mutable: row.__setattr__(key, [])
def setup_row(index_values, table, row_data, txn, reflist, schema, idl, validator_adapter, errors, old_row=None): # Initialize the flag for row to check if it is new row is_new = False # Check if row exists in DB if old_row is not None: row = old_row else: row = utils.index_to_row(index_values, schema.ovs_tables[table], idl.tables[table]) # Create a new row if not found in DB if row is None: if table in immutable_tables: # Do NOT add row in Immutable table_data return (None, False) row = txn.insert(idl.tables[table]) is_new = True # Routes are special case - only static routes can be updated if table == 'Route': if not is_new and row.__getattr__('from') != 'static': return (None, False) elif is_new: row.__setattr__('from', 'static') references = schema.ovs_tables[table].references children = schema.ovs_tables[table].children try: request_type = REQUEST_TYPE_CREATE if is_new else REQUEST_TYPE_UPDATE get_all_errors = True # Check for back-references and remove it from the row data since # it will be checked upon recursive call anyways. _row_data = deepcopy(row_data) for data in row_data: if data in children and data not in references: del _row_data[data] results = verify.verify_config_data(_row_data, table, schema, request_type, get_all_errors) except DataValidationFailed as e: errors.extend(e.detail) config_rows = schema.ovs_tables[table].config config_keys = config_rows.keys() # Iterate over all config keys for key in config_keys: # Ignore if row is existing and column is immutable if not is_new and not config_rows[key].mutable: continue # Set the column values from user config if key not in row_data and not is_new: empty_val = utils.get_empty_by_basic_type(row.__getattr__(key)) row.__setattr__(key, empty_val) elif (key in row_data and (is_new or row.__getattr__(key) != row_data[key])): row.__setattr__(key, row_data[key]) # Delete all the keys that don't exist for key in children: child_table = references[key].ref_table \ if key in references else key # Check if table is immutable if child_table in immutable_tables: if not is_new and (key not in row_data or not row_data[key]): # Deep clean-up children, even if missing or empty, # Ignore if immutable if key in references: kv_type = references[key].kv_type if kv_type: rowlist = row.__getattr__(key).values() else: rowlist = row.__getattr__(key) clean_subtree(child_table, rowlist, txn, schema, idl, validator_adapter) else: clean_subtree(child_table, [], txn, schema, idl, validator_adapter, row) continue # forward child references if key in references: table_schema = schema.ovs_tables[table] reference = table_schema.references[key] kv_type = reference.kv_type if not is_new and key not in row_data: if kv_type: row.__setattr__(key, {}) else: row.__setattr__(key, []) else: # back-references if child_table not in row_data: new_data = {} else: new_data = row_data[child_table] remove_deleted_rows(child_table, new_data, txn, schema, idl, validator_adapter, row) # set up children that exist for key in children: child_table = references[key].ref_table \ if key in references else key if key in row_data: if key in references: # forward referenced children table_schema = schema.ovs_tables[table] reference = table_schema.references[key] kv_type = reference.kv_type kv_key_type = None current_child_rows = {} # Key-value type children (Dict type) # Example - BGP_Router is KV-Type child of VRF if kv_type: kv_key_type = reference.kv_key_type if not is_new: current_child_rows = row.__getattr__(key) child_reference_list = {} # Regular children else: child_reference_list = [] # Iterate over each child_row for child_index, child_row_data in row_data[key].iteritems(): current_row = None if kv_type: if (kv_key_type is not None and kv_key_type.name == 'integer'): child_index = int(child_index) if child_index in current_child_rows: current_row = current_child_rows[child_index] child_index_values = [] else: child_index_values = utils.escaped_split(child_index) (child_row, is_child_new) = setup_row(child_index_values, child_table, child_row_data, txn, reflist, schema, idl, validator_adapter, errors, current_row) if child_row is None: continue op = REQUEST_TYPE_CREATE if is_child_new else \ REQUEST_TYPE_UPDATE validator_adapter.add_resource_op(op, child_row, child_table, row, table) if kv_type: child_reference_list.update({child_index: child_row}) else: child_reference_list.append(child_row) # Save this in global reflist reflist[(child_table, child_index)] = (child_row, is_child_new) if child_table not in immutable_tables: row.__setattr__(key, child_reference_list) else: # backward referenced children parent_column = None references = schema.ovs_tables[child_table].references for col_name, col_value in references.iteritems(): if col_value.relation == 'parent': parent_column = col_name break # Iterate over each child_row for child_index, child_row_data in row_data[key].iteritems(): child_index_values = utils.escaped_split(child_index) (child_row, is_child_new) = setup_row(child_index_values, child_table, child_row_data, txn, reflist, schema, idl, validator_adapter, errors) if child_row is None: continue op = REQUEST_TYPE_CREATE if is_child_new else \ REQUEST_TYPE_UPDATE validator_adapter.add_resource_op(op, child_row, child_table, row, table) # Set the references column in child row if parent_column is not None and is_child_new: child_row.__setattr__(parent_column, row) # save this in global reflist reflist[(child_table, child_index)] = (child_row, is_child_new) return (row, is_new)
def setup_row(rowdata, table_name, schema, idl, txn): """ set up rows recursively """ row_index = rowdata.keys()[0] row_data = rowdata.values()[0] table_schema = schema.ovs_tables[table_name] idl_table = idl.tables[table_name] # get row reference from table _new = False row = index_to_row(row_index, table_schema, idl_table) if row is None: row = txn.insert(idl.tables[table_name]) _new = True # NOTE: populate configuration data config_keys = table_schema.config.keys() for key in config_keys: # TODO: return error if trying to set an immutable column # skip if trying to set an immutable column for an existing row if not _new and table_schema.config[key].mutable is False: continue if key not in row_data: # skip if it's a new row if _new or row.__getattr__(key) is None: continue else: # set the right empty value value = utils.get_empty_by_basic_type(row.__getattr__(key)) else: value = row_data[key] row.__setattr__(key, value) # NOTE: populate non-config index columns if _new: for key in table_schema.indexes: if key is 'uuid': continue if key not in table_schema.config.keys(): row.__setattr__(key, row_data[key]) # NOTE: set up child references for key in table_schema.children: # NOTE: 'forward' type children if key in table_schema.references: child_table_name = table_schema.references[key].ref_table # skip immutable column if not _new and not table_schema.references[key].mutable: continue # set up empty reference list if key not in row_data: if _new or row.__getattr__(key) is None: continue else: value = utils.get_empty_by_basic_type(row.__getattr__(key)) row.__setattr__(key, value) else: new_data = row_data[key] # {key:UUID} type of reference check kv_type = table_schema.references[key].kv_type # if not a new row, delete non-existent child references if not _new: current_list = row.__getattr__(key) if current_list: if kv_type: current_list = current_list.values() delete_list = [] for item in current_list: index = utils.row_to_index(item, child_table_name, schema, idl) if index not in new_data: delete_list.append(item) if delete_list: for item in delete_list: _delete(item, child_table_name, schema, idl, txn) # setup children children = {} for index, child_data in new_data.iteritems(): _child = setup_row({index:child_data}, child_table_name, schema, idl, txn) children.update(_child) # NOTE: If the child table doesn't have indexes, replace json index # with row.uuid if not schema.ovs_tables[child_table_name].index_columns: for k,v in children.iteritems(): new_data[v.uuid] = new_data[k] del new_data[k] if kv_type: if table_schema.references[key].kv_key_type.name == 'integer': tmp = {} for k,v in children.iteritems(): tmp[int(k)] = v children = tmp row.__setattr__(key, children) else: row.__setattr__(key, children.values()) # Backward reference else: # get list of all 'backward' references column_name = None for x, y in schema.ovs_tables[key].references.iteritems(): if y.relation == OVSDB_SCHEMA_PARENT: column_name = x break # delete non-existent rows # get list of all rows with same parent if not _new: current_list = [] for item in idl.tables[key].rows.itervalues(): parent = item.__getattr__(column_name) if parent.uuid == row.uuid: current_list.append(item) new_data = None if key in row_data: new_data = row_data[key] if current_list: delete_list = [] if new_data is None: delete_list = current_list else: for item in current_list: index = utils.row_to_index(item,key, schema, idl) if index not in new_data: delete_list.append(item) if delete_list: _delete(delete_list, key, schema, idl, txn) # set up children rows if new_data is not None: for x,y in new_data.iteritems(): child = setup_row({x:y}, key, schema, idl, txn) # fill the parent reference column child.values()[0].__setattr__(column_name, row) return {row_index:row}