Enhance support for policy transaction
Add support for non first level objects, such as segment ports Change-Id: I55f7f7f1921f7ff9c57adb93355943ffc7a9c542
This commit is contained in:
@@ -158,3 +158,91 @@ class TestPolicyTransaction(policy_testcase.TestPolicyApi):
|
|||||||
'IpAddressPool': pool}]}
|
'IpAddressPool': pool}]}
|
||||||
|
|
||||||
self.assert_infra_patch_call(expected_body)
|
self.assert_infra_patch_call(expected_body)
|
||||||
|
|
||||||
|
def test_groups_only(self):
|
||||||
|
|
||||||
|
g1 = {'resource_type': 'Group', 'id': 'group1',
|
||||||
|
'display_name': 'g1',
|
||||||
|
'description': 'first group'}
|
||||||
|
g2 = {'resource_type': 'Group', 'id': 'group2',
|
||||||
|
'description': 'second group',
|
||||||
|
'display_name': 'g2'}
|
||||||
|
d1 = {'resource_type': 'Domain', 'id': 'domain1'}
|
||||||
|
|
||||||
|
d2 = {'resource_type': 'Domain', 'id': 'domain2'}
|
||||||
|
|
||||||
|
with trans.NsxPolicyTransaction():
|
||||||
|
|
||||||
|
for d in (d1, d2):
|
||||||
|
d['children'] = []
|
||||||
|
|
||||||
|
for g in (g1, g2):
|
||||||
|
self.policy_lib.group.create_or_overwrite(
|
||||||
|
g['display_name'],
|
||||||
|
d['id'],
|
||||||
|
g['id'],
|
||||||
|
g['description'])
|
||||||
|
|
||||||
|
d['children'].append({'resource_type': 'ChildGroup',
|
||||||
|
'Group': g})
|
||||||
|
|
||||||
|
expected_body = {'resource_type': 'Infra',
|
||||||
|
'children': [{'resource_type': 'ChildDomain',
|
||||||
|
'Domain': d1},
|
||||||
|
{'resource_type': 'ChildDomain',
|
||||||
|
'Domain': d2}]}
|
||||||
|
|
||||||
|
self.assert_infra_patch_call(expected_body)
|
||||||
|
|
||||||
|
def test_segment_ports(self):
|
||||||
|
|
||||||
|
port1 = {'id': 'port_on_seg1',
|
||||||
|
'resource_type': 'SegmentPort',
|
||||||
|
'display_name': 'port_on_seg1',
|
||||||
|
'attachment': {'type': 'VIF',
|
||||||
|
'app_id': 'app1',
|
||||||
|
'traffic_tag': 5}
|
||||||
|
}
|
||||||
|
|
||||||
|
port2 = {'id': 'port1_on_seg2',
|
||||||
|
'resource_type': 'SegmentPort',
|
||||||
|
'display_name': 'port_on_seg2',
|
||||||
|
'attachment': {'type': 'CHILD',
|
||||||
|
'app_id': 'app2',
|
||||||
|
'traffic_tag': None}
|
||||||
|
}
|
||||||
|
|
||||||
|
seg1 = {'id': 'seg1',
|
||||||
|
'resource_type': 'Segment',
|
||||||
|
'children': [{'resource_type': 'ChildSegmentPort',
|
||||||
|
'SegmentPort': port1}]}
|
||||||
|
seg2 = {'id': 'seg2',
|
||||||
|
'resource_type': 'Segment',
|
||||||
|
'children': [{'resource_type': 'ChildSegmentPort',
|
||||||
|
'SegmentPort': port2}]}
|
||||||
|
|
||||||
|
with trans.NsxPolicyTransaction():
|
||||||
|
|
||||||
|
self.policy_lib.segment_port.create_or_overwrite(
|
||||||
|
port1['display_name'],
|
||||||
|
seg1['id'],
|
||||||
|
port1['id'],
|
||||||
|
attachment_type=port1['attachment']['type'],
|
||||||
|
app_id=port1['attachment']['app_id'],
|
||||||
|
traffic_tag=port1['attachment']['traffic_tag'])
|
||||||
|
|
||||||
|
self.policy_lib.segment_port.create_or_overwrite(
|
||||||
|
port2['display_name'],
|
||||||
|
seg2['id'],
|
||||||
|
port2['id'],
|
||||||
|
attachment_type=port2['attachment']['type'],
|
||||||
|
app_id=port2['attachment']['app_id'],
|
||||||
|
traffic_tag=port2['attachment']['traffic_tag'])
|
||||||
|
|
||||||
|
expected_body = {'resource_type': 'Infra',
|
||||||
|
'children': [{'resource_type': 'ChildSegment',
|
||||||
|
'Segment': seg1},
|
||||||
|
{'resource_type': 'ChildSegment',
|
||||||
|
'Segment': seg2}]}
|
||||||
|
|
||||||
|
self.assert_infra_patch_call(expected_body)
|
||||||
|
|||||||
@@ -96,15 +96,28 @@ class NsxPolicyTransaction(object):
|
|||||||
|
|
||||||
self.defs = sorted_defs
|
self.defs = sorted_defs
|
||||||
|
|
||||||
|
def _build_wrapper_dict(self, resource_class, node):
|
||||||
|
return {'resource_type': 'Child%s' % resource_class,
|
||||||
|
resource_class: node}
|
||||||
|
|
||||||
def _find_parent_in_dict(self, d, resource_def, level=1):
|
def _find_parent_in_dict(self, d, resource_def, level=1):
|
||||||
|
|
||||||
if len(resource_def.path_defs()) <= level:
|
if len(resource_def.path_defs()) <= level:
|
||||||
return
|
return
|
||||||
|
|
||||||
parent_type = resource_def.path_defs()[level]
|
parent_type = resource_def.path_defs()[level]
|
||||||
|
|
||||||
is_leaf = (level + 1 == len(resource_def.path_defs()))
|
is_leaf = (level + 1 == len(resource_def.path_defs()))
|
||||||
resource_type = parent_type.resource_type()
|
resource_type = parent_type.resource_type()
|
||||||
|
resource_class = parent_type.resource_class()
|
||||||
parent_id = resource_def.get_attr(resource_def.path_ids[level])
|
parent_id = resource_def.get_attr(resource_def.path_ids[level])
|
||||||
|
|
||||||
|
def create_missing_node():
|
||||||
|
node = {'resource_type': resource_type,
|
||||||
|
'id': parent_id,
|
||||||
|
'children': []}
|
||||||
|
return self._build_wrapper_dict(resource_class, node), node
|
||||||
|
|
||||||
# iterate over all objects in d, and look for resource type
|
# iterate over all objects in d, and look for resource type
|
||||||
for child in d:
|
for child in d:
|
||||||
if resource_type in child and child[resource_type]:
|
if resource_type in child and child[resource_type]:
|
||||||
@@ -113,17 +126,20 @@ class NsxPolicyTransaction(object):
|
|||||||
if parent['id'] == parent_id:
|
if parent['id'] == parent_id:
|
||||||
if is_leaf:
|
if is_leaf:
|
||||||
return parent
|
return parent
|
||||||
if 'children' in parent:
|
if 'children' not in parent:
|
||||||
|
parent['children'] = []
|
||||||
|
|
||||||
return self._find_parent_in_dict(
|
return self._find_parent_in_dict(
|
||||||
parent['children'], resource_def, level + 1)
|
parent['children'], resource_def, level + 1)
|
||||||
|
|
||||||
# Parent not found - for now, raise an exception
|
# Parent not found - create a node for missing parent
|
||||||
# Support for this will come later
|
wrapper, node = create_missing_node()
|
||||||
# TODO(annak): remove this when missing parent body is
|
d.append(wrapper)
|
||||||
# created on demand
|
if is_leaf:
|
||||||
raise NsxPolicyTransactionException(
|
# This is the last parent that needs creation
|
||||||
"Transactional create is supported for infra level"
|
return node
|
||||||
" objects and their children")
|
return self._find_parent_in_dict(node['children'], resource_def,
|
||||||
|
level + 1)
|
||||||
|
|
||||||
def apply_defs(self):
|
def apply_defs(self):
|
||||||
# TODO(annak): find longest common URL, for now always
|
# TODO(annak): find longest common URL, for now always
|
||||||
@@ -136,7 +152,8 @@ class NsxPolicyTransaction(object):
|
|||||||
|
|
||||||
top_def = self.defs[0]
|
top_def = self.defs[0]
|
||||||
url = top_def.get_resource_path()
|
url = top_def.get_resource_path()
|
||||||
body = {'resource_type': top_def.resource_type()}
|
body = {'resource_type': top_def.resource_type(),
|
||||||
|
'children': []}
|
||||||
# iterate over defs (except top level def)
|
# iterate over defs (except top level def)
|
||||||
for resource_def in self.defs[1:]:
|
for resource_def in self.defs[1:]:
|
||||||
parent_dict = None
|
parent_dict = None
|
||||||
@@ -145,16 +162,16 @@ class NsxPolicyTransaction(object):
|
|||||||
resource_def)
|
resource_def)
|
||||||
|
|
||||||
if not parent_dict:
|
if not parent_dict:
|
||||||
|
# Top level resource
|
||||||
parent_dict = body
|
parent_dict = body
|
||||||
|
|
||||||
if 'children' not in parent_dict:
|
if 'children' not in parent_dict:
|
||||||
parent_dict['children'] = []
|
parent_dict['children'] = []
|
||||||
|
|
||||||
resource_class = resource_def.resource_class()
|
resource_class = resource_def.resource_class()
|
||||||
parent_dict['children'].append({
|
parent_dict['children'].append(
|
||||||
'resource_type': 'Child%s' % resource_class,
|
self._build_wrapper_dict(resource_class,
|
||||||
resource_class: resource_def.get_obj_dict()
|
resource_def.get_obj_dict()))
|
||||||
})
|
|
||||||
|
|
||||||
if body:
|
if body:
|
||||||
self.client.patch(url, body)
|
self.client.patch(url, body)
|
||||||
|
|||||||
Reference in New Issue
Block a user