Merge "Update Cisco N1KV plugin to VSM REST api calls"
This commit is contained in:
commit
f76c1909a0
@ -105,6 +105,7 @@ MAPPING = 'mapping'
|
||||
SEGMENTS = 'segments'
|
||||
SEGMENT = 'segment'
|
||||
BRIDGE_DOMAIN_SUFFIX = '_bd'
|
||||
LOGICAL_NETWORK_SUFFIX = '_log_net'
|
||||
ENCAPSULATION_PROFILE_SUFFIX = '_profile'
|
||||
|
||||
UUID_LENGTH = 36
|
||||
|
@ -849,6 +849,7 @@ def create_network_profile(db_session, network_profile):
|
||||
kwargs["multicast_ip_range"] = network_profile[
|
||||
"multicast_ip_range"]
|
||||
kwargs["segment_range"] = network_profile["segment_range"]
|
||||
kwargs["sub_type"] = network_profile["sub_type"]
|
||||
elif network_profile["segment_type"] == c_const.NETWORK_TYPE_TRUNK:
|
||||
kwargs["sub_type"] = network_profile["sub_type"]
|
||||
net_profile = n1kv_models_v2.NetworkProfile(**kwargs)
|
||||
|
@ -30,7 +30,7 @@ MEMBER_SEGMENTS = 'n1kv:member_segments'
|
||||
|
||||
EXTENDED_ATTRIBUTES_2_0 = {
|
||||
'networks': {
|
||||
PROFILE_ID: {'allow_post': True, 'allow_put': True,
|
||||
PROFILE_ID: {'allow_post': True, 'allow_put': False,
|
||||
'validate': {'type:regex': attributes.UUID_PATTERN},
|
||||
'default': attributes.ATTR_NOT_SPECIFIED,
|
||||
'is_visible': True},
|
||||
@ -48,7 +48,7 @@ EXTENDED_ATTRIBUTES_2_0 = {
|
||||
'is_visible': True},
|
||||
},
|
||||
'ports': {
|
||||
PROFILE_ID: {'allow_post': True, 'allow_put': True,
|
||||
PROFILE_ID: {'allow_post': True, 'allow_put': False,
|
||||
'validate': {'type:regex': attributes.UUID_PATTERN},
|
||||
'default': attributes.ATTR_NOT_SPECIFIED,
|
||||
'is_visible': True}
|
||||
|
@ -32,20 +32,18 @@ RESOURCE_ATTRIBUTE_MAP = {
|
||||
'is_visible': True},
|
||||
'name': {'allow_post': True, 'allow_put': True,
|
||||
'is_visible': True, 'default': ''},
|
||||
'segment_type': {'allow_post': True, 'allow_put': True,
|
||||
'segment_type': {'allow_post': True, 'allow_put': False,
|
||||
'is_visible': True, 'default': ''},
|
||||
'sub_type': {'allow_post': True, 'allow_put': True,
|
||||
'sub_type': {'allow_post': True, 'allow_put': False,
|
||||
'is_visible': True,
|
||||
'default': attributes.ATTR_NOT_SPECIFIED},
|
||||
'segment_range': {'allow_post': True, 'allow_put': True,
|
||||
'is_visible': True, 'default': ''},
|
||||
'sub_type': {'allow_post': True, 'allow_put': True,
|
||||
'is_visible': True, 'default': ''},
|
||||
'multicast_ip_range': {'allow_post': True, 'allow_put': True,
|
||||
'is_visible': True, 'default': '0.0.0.0'},
|
||||
'multicast_ip_index': {'allow_post': False, 'allow_put': False,
|
||||
'is_visible': False, 'default': '0'},
|
||||
'physical_network': {'allow_post': True, 'allow_put': True,
|
||||
'physical_network': {'allow_post': True, 'allow_put': False,
|
||||
'is_visible': True, 'default': ''},
|
||||
'tenant_id': {'allow_post': True, 'allow_put': False,
|
||||
'is_visible': False, 'default': ''},
|
||||
|
@ -126,11 +126,8 @@ class Client(object):
|
||||
|
||||
# Define paths for the URI where the client connects for HTTP requests.
|
||||
port_profiles_path = "/virtual-port-profile"
|
||||
network_segments_path = "/network-segment"
|
||||
network_segment_path = "/network-segment/%s"
|
||||
network_segment_pools_path = "/network-segment-pool"
|
||||
network_segment_pool_path = "/network-segment-pool/%s"
|
||||
ip_pools_path = "/ip-pool-template"
|
||||
ip_pool_path = "/ip-pool-template/%s"
|
||||
ports_path = "/kvm/vm-network/%s/ports"
|
||||
port_path = "/kvm/vm-network/%s/ports/%s"
|
||||
@ -138,7 +135,6 @@ class Client(object):
|
||||
vm_network_path = "/kvm/vm-network/%s"
|
||||
bridge_domains_path = "/kvm/bridge-domain"
|
||||
bridge_domain_path = "/kvm/bridge-domain/%s"
|
||||
logical_networks_path = "/logical-network"
|
||||
logical_network_path = "/logical-network/%s"
|
||||
events_path = "/kvm/events"
|
||||
clusters_path = "/cluster"
|
||||
@ -179,9 +175,10 @@ class Client(object):
|
||||
:param network: network dict
|
||||
:param overlay_subtype: string representing subtype of overlay network
|
||||
"""
|
||||
body = {'name': network['name'] + c_const.BRIDGE_DOMAIN_SUFFIX,
|
||||
body = {'name': network['id'] + c_const.BRIDGE_DOMAIN_SUFFIX,
|
||||
'segmentId': network[providernet.SEGMENTATION_ID],
|
||||
'subType': overlay_subtype}
|
||||
'subType': overlay_subtype,
|
||||
'tenantId': network['tenant_id']}
|
||||
if overlay_subtype == c_const.NETWORK_SUBTYPE_NATIVE_VXLAN:
|
||||
body['groupIp'] = network[n1kv_profile.MULTICAST_IP]
|
||||
return self._post(self.bridge_domains_path,
|
||||
@ -193,7 +190,7 @@ class Client(object):
|
||||
|
||||
:param name: name of the bridge domain to be deleted
|
||||
"""
|
||||
return self._delete(self.bridge_domain_path % (name))
|
||||
return self._delete(self.bridge_domain_path % name)
|
||||
|
||||
def create_network_segment(self, network, network_profile):
|
||||
"""
|
||||
@ -202,14 +199,15 @@ class Client(object):
|
||||
:param network: network dict
|
||||
:param network_profile: network profile dict
|
||||
"""
|
||||
body = {'name': network['name'],
|
||||
body = {'publishName': network['name'],
|
||||
'description': network['name'],
|
||||
'id': network['id'],
|
||||
'mode': 'access',
|
||||
'networkSegmentPool': network_profile['name'], }
|
||||
'tenantId': network['tenant_id'],
|
||||
'networkSegmentPool': network_profile['id'], }
|
||||
if network[providernet.NETWORK_TYPE] == c_const.NETWORK_TYPE_VLAN:
|
||||
body['vlan'] = network[providernet.SEGMENTATION_ID]
|
||||
elif network[providernet.NETWORK_TYPE] == c_const.NETWORK_TYPE_OVERLAY:
|
||||
body['bridgeDomain'] = (network['name'] +
|
||||
body['bridgeDomain'] = (network['id'] +
|
||||
c_const.BRIDGE_DOMAIN_SUFFIX)
|
||||
if network_profile['segment_type'] == c_const.NETWORK_TYPE_TRUNK:
|
||||
body['mode'] = c_const.NETWORK_TYPE_TRUNK
|
||||
@ -218,88 +216,99 @@ class Client(object):
|
||||
body['addSegments'] = network['add_segment_list']
|
||||
body['delSegments'] = network['del_segment_list']
|
||||
else:
|
||||
body['encapProfile'] = (network['name'] +
|
||||
body['encapProfile'] = (network['id'] +
|
||||
c_const.ENCAPSULATION_PROFILE_SUFFIX)
|
||||
else:
|
||||
body['mode'] = 'access'
|
||||
body['segmentType'] = network_profile['segment_type']
|
||||
return self._post(self.network_segments_path,
|
||||
return self._post(self.network_segment_path % network['id'],
|
||||
body=body)
|
||||
|
||||
def update_network_segment(self, network_segment_name, body):
|
||||
def update_network_segment(self, network_segment_id, body):
|
||||
"""
|
||||
Update a network segment on the VSM.
|
||||
|
||||
Network segment on VSM can be updated to associate it with an ip-pool
|
||||
or update its description and segment id.
|
||||
|
||||
:param network_segment_name: name of the network segment
|
||||
:param network_segment_id: UUID representing the network segment
|
||||
:param body: dict of arguments to be updated
|
||||
"""
|
||||
return self._post(self.network_segment_path % (network_segment_name),
|
||||
return self._post(self.network_segment_path % network_segment_id,
|
||||
body=body)
|
||||
|
||||
def delete_network_segment(self, network_segment_name):
|
||||
def delete_network_segment(self, network_segment_id):
|
||||
"""
|
||||
Delete a network segment on the VSM.
|
||||
|
||||
:param network_segment_name: name of the network segment
|
||||
:param network_segment_id: UUID representing the network segment
|
||||
"""
|
||||
return self._delete(self.network_segment_path % (network_segment_name))
|
||||
return self._delete(self.network_segment_path % network_segment_id)
|
||||
|
||||
def create_logical_network(self, network_profile):
|
||||
def create_logical_network(self, network_profile, tenant_id):
|
||||
"""
|
||||
Create a logical network on the VSM.
|
||||
|
||||
:param network_profile: network profile dict
|
||||
:param tenant_id: UUID representing the tenant
|
||||
"""
|
||||
LOG.debug(_("Logical network"))
|
||||
body = {'name': network_profile['name']}
|
||||
return self._post(self.logical_networks_path,
|
||||
body = {'description': network_profile['name'],
|
||||
'tenantId': tenant_id}
|
||||
logical_network_name = (network_profile['id'] +
|
||||
c_const.LOGICAL_NETWORK_SUFFIX)
|
||||
return self._post(self.logical_network_path % logical_network_name,
|
||||
body=body)
|
||||
|
||||
def delete_logical_network(self, network_profile):
|
||||
def delete_logical_network(self, logical_network_name):
|
||||
"""
|
||||
Delete a logical network on VSM.
|
||||
|
||||
:param network_profile: network profile dict
|
||||
:param logical_network_name: string representing name of the logical
|
||||
network
|
||||
"""
|
||||
return self._delete(
|
||||
self.logical_network_path % (network_profile['name']))
|
||||
self.logical_network_path % logical_network_name)
|
||||
|
||||
def create_network_segment_pool(self, network_profile):
|
||||
def create_network_segment_pool(self, network_profile, tenant_id):
|
||||
"""
|
||||
Create a network segment pool on the VSM.
|
||||
|
||||
:param network_profile: network profile dict
|
||||
:param tenant_id: UUID representing the tenant
|
||||
"""
|
||||
LOG.debug(_("network_segment_pool"))
|
||||
logical_network_name = (network_profile['id'] +
|
||||
c_const.LOGICAL_NETWORK_SUFFIX)
|
||||
body = {'name': network_profile['name'],
|
||||
'description': network_profile['name'],
|
||||
'id': network_profile['id'],
|
||||
'logicalNetwork': network_profile['name']}
|
||||
return self._post(self.network_segment_pools_path,
|
||||
body=body)
|
||||
'logicalNetwork': logical_network_name,
|
||||
'tenantId': tenant_id}
|
||||
return self._post(
|
||||
self.network_segment_pool_path % network_profile['id'],
|
||||
body=body)
|
||||
|
||||
def update_network_segment_pool(self, network_segment_pool, body):
|
||||
def update_network_segment_pool(self, network_profile):
|
||||
"""
|
||||
Update a network segment pool on the VSM.
|
||||
|
||||
:param network_segment_pool: string representing the name of network
|
||||
segment pool to be updated
|
||||
:param body: dictionary representing key values of network segment
|
||||
pool which need to be updated
|
||||
:param network_profile: network profile dict
|
||||
"""
|
||||
body = {'name': network_profile['name'],
|
||||
'description': network_profile['name']}
|
||||
return self._post(self.network_segment_pool_path %
|
||||
(network_segment_pool), body=body)
|
||||
network_profile['id'], body=body)
|
||||
|
||||
def delete_network_segment_pool(self, network_segment_pool_name):
|
||||
def delete_network_segment_pool(self, network_segment_pool_id):
|
||||
"""
|
||||
Delete a network segment pool on the VSM.
|
||||
|
||||
:param network_segment_pool_name: name of the network segment pool
|
||||
:param network_segment_pool_id: UUID representing the network
|
||||
segment pool
|
||||
"""
|
||||
return self._delete(self.network_segment_pool_path %
|
||||
(network_segment_pool_name))
|
||||
network_segment_pool_id)
|
||||
|
||||
def create_ip_pool(self, subnet):
|
||||
"""
|
||||
@ -328,38 +337,38 @@ class Client(object):
|
||||
body = {'addressRangeStart': address_range_start,
|
||||
'addressRangeEnd': address_range_end,
|
||||
'ipAddressSubnet': netmask,
|
||||
'name': subnet['name'],
|
||||
'description': subnet['name'],
|
||||
'gateway': subnet['gateway_ip'],
|
||||
'networkAddress': network_address}
|
||||
return self._post(self.ip_pools_path,
|
||||
'networkAddress': network_address,
|
||||
'tenantId': subnet['tenant_id']}
|
||||
return self._post(self.ip_pool_path % subnet['id'],
|
||||
body=body)
|
||||
|
||||
def delete_ip_pool(self, subnet_name):
|
||||
def delete_ip_pool(self, subnet_id):
|
||||
"""
|
||||
Delete an ip-pool on the VSM.
|
||||
|
||||
:param subnet_name: name of the subnet
|
||||
:param subnet_id: UUID representing the subnet
|
||||
"""
|
||||
return self._delete(self.ip_pool_path % (subnet_name))
|
||||
return self._delete(self.ip_pool_path % subnet_id)
|
||||
|
||||
def create_vm_network(self,
|
||||
port,
|
||||
vm_network_name,
|
||||
policy_profile,
|
||||
network_name):
|
||||
policy_profile):
|
||||
"""
|
||||
Create a VM network on the VSM.
|
||||
|
||||
:param port: port dict
|
||||
:param vm_network_name: name of the VM network
|
||||
:param policy_profile: policy profile dict
|
||||
:param network_name: string representing the name of the network
|
||||
"""
|
||||
body = {'name': vm_network_name,
|
||||
'networkSegmentId': port['network_id'],
|
||||
'networkSegment': network_name,
|
||||
'networkSegment': port['network_id'],
|
||||
'portProfile': policy_profile['name'],
|
||||
'portProfileId': policy_profile['id'],
|
||||
'tenantId': port['tenant_id'],
|
||||
}
|
||||
return self._post(self.vm_networks_path,
|
||||
body=body)
|
||||
@ -370,7 +379,7 @@ class Client(object):
|
||||
|
||||
:param vm_network_name: name of the VM network
|
||||
"""
|
||||
return self._delete(self.vm_network_path % (vm_network_name))
|
||||
return self._delete(self.vm_network_path % vm_network_name)
|
||||
|
||||
def create_n1kv_port(self, port, vm_network_name):
|
||||
"""
|
||||
@ -381,7 +390,9 @@ class Client(object):
|
||||
"""
|
||||
body = {'id': port['id'],
|
||||
'macAddress': port['mac_address']}
|
||||
return self._post(self.ports_path % (vm_network_name),
|
||||
if port.get('fixed_ips'):
|
||||
body['ipAddress'] = port['fixed_ips'][0]['ip_address']
|
||||
return self._post(self.ports_path % vm_network_name,
|
||||
body=body)
|
||||
|
||||
def update_n1kv_port(self, vm_network_name, port_id, body):
|
||||
@ -394,7 +405,7 @@ class Client(object):
|
||||
:param port_id: UUID of the port
|
||||
:param body: dict of the arguments to be updated
|
||||
"""
|
||||
return self._post(self.port_path % ((vm_network_name), (port_id)),
|
||||
return self._post(self.port_path % (vm_network_name, port_id),
|
||||
body=body)
|
||||
|
||||
def delete_n1kv_port(self, vm_network_name, port_id):
|
||||
@ -404,7 +415,7 @@ class Client(object):
|
||||
:param vm_network_name: name of the VM network which imports this port
|
||||
:param port_id: UUID of the port
|
||||
"""
|
||||
return self._delete(self.port_path % ((vm_network_name), (port_id)))
|
||||
return self._delete(self.port_path % (vm_network_name, port_id))
|
||||
|
||||
def _do_request(self, method, action, body=None,
|
||||
headers=None):
|
||||
@ -484,7 +495,7 @@ class Client(object):
|
||||
"""
|
||||
if not format:
|
||||
format = self.format
|
||||
return "application/%s" % (format)
|
||||
return "application/%s" % format
|
||||
|
||||
def _delete(self, action, body=None, headers=None):
|
||||
return self._do_request("DELETE", action, body=body,
|
||||
@ -548,7 +559,7 @@ class Client(object):
|
||||
:param body: mapping dictionary
|
||||
"""
|
||||
return self._post(self.encap_profile_path
|
||||
% (profile_name), body=body)
|
||||
% profile_name, body=body)
|
||||
|
||||
def delete_encapsulation_profile(self, name):
|
||||
"""
|
||||
@ -556,4 +567,4 @@ class Client(object):
|
||||
|
||||
:param name: name of the encapsulation profile to be deleted
|
||||
"""
|
||||
return self._delete(self.encap_profile_path % (name))
|
||||
return self._delete(self.encap_profile_path % name)
|
||||
|
@ -697,15 +697,16 @@ class N1kvNeutronPluginV2(db_base_plugin_v2.NeutronDbPluginV2,
|
||||
|
||||
return profile_id
|
||||
|
||||
def _send_create_logical_network_request(self, network_profile):
|
||||
def _send_create_logical_network_request(self, network_profile, tenant_id):
|
||||
"""
|
||||
Send create logical network request to VSM.
|
||||
|
||||
:param network_profile: network profile dictionary
|
||||
:param tenant_id: UUID representing the tenant
|
||||
"""
|
||||
LOG.debug(_('_send_create_logical_network'))
|
||||
n1kvclient = n1kv_client.Client()
|
||||
n1kvclient.create_logical_network(network_profile)
|
||||
n1kvclient.create_logical_network(network_profile, tenant_id)
|
||||
|
||||
def _send_delete_logical_network_request(self, network_profile):
|
||||
"""
|
||||
@ -715,7 +716,9 @@ class N1kvNeutronPluginV2(db_base_plugin_v2.NeutronDbPluginV2,
|
||||
"""
|
||||
LOG.debug('_send_delete_logical_network')
|
||||
n1kvclient = n1kv_client.Client()
|
||||
n1kvclient.delete_logical_network(network_profile)
|
||||
logical_network_name = (network_profile['id'] +
|
||||
c_const.LOGICAL_NETWORK_SUFFIX)
|
||||
n1kvclient.delete_logical_network(logical_network_name)
|
||||
|
||||
def _send_create_network_profile_request(self, context, profile):
|
||||
"""
|
||||
@ -726,7 +729,17 @@ class N1kvNeutronPluginV2(db_base_plugin_v2.NeutronDbPluginV2,
|
||||
"""
|
||||
LOG.debug(_('_send_create_network_profile_request: %s'), profile['id'])
|
||||
n1kvclient = n1kv_client.Client()
|
||||
n1kvclient.create_network_segment_pool(profile)
|
||||
n1kvclient.create_network_segment_pool(profile, context.tenant_id)
|
||||
|
||||
def _send_update_network_profile_request(self, profile):
|
||||
"""
|
||||
Send update network profile request to VSM.
|
||||
|
||||
:param profile: network profile dictionary
|
||||
"""
|
||||
LOG.debug(_('_send_update_network_profile_request: %s'), profile['id'])
|
||||
n1kvclient = n1kv_client.Client()
|
||||
n1kvclient.update_network_segment_pool(profile)
|
||||
|
||||
def _send_delete_network_profile_request(self, profile):
|
||||
"""
|
||||
@ -737,7 +750,7 @@ class N1kvNeutronPluginV2(db_base_plugin_v2.NeutronDbPluginV2,
|
||||
LOG.debug(_('_send_delete_network_profile_request: %s'),
|
||||
profile['name'])
|
||||
n1kvclient = n1kv_client.Client()
|
||||
n1kvclient.delete_network_segment_pool(profile['name'])
|
||||
n1kvclient.delete_network_segment_pool(profile['id'])
|
||||
|
||||
def _send_create_network_request(self, context, network, segment_pairs):
|
||||
"""
|
||||
@ -786,9 +799,9 @@ class N1kvNeutronPluginV2(db_base_plugin_v2.NeutronDbPluginV2,
|
||||
profile = n1kv_db_v2.get_network_profile(
|
||||
db_session, network[n1kv_profile.PROFILE_ID])
|
||||
n1kvclient = n1kv_client.Client()
|
||||
body = {'name': network['name'],
|
||||
body = {'publishName': network['name'],
|
||||
'id': network['id'],
|
||||
'networkDefinition': profile['name'],
|
||||
'networkSegmentPool': profile['id'],
|
||||
'vlan': network[providernet.SEGMENTATION_ID],
|
||||
'mode': 'access',
|
||||
'segmentType': profile['segment_type'],
|
||||
@ -806,7 +819,7 @@ class N1kvNeutronPluginV2(db_base_plugin_v2.NeutronDbPluginV2,
|
||||
LOG.debug(_('add_segments=%s'), body['addSegments'])
|
||||
LOG.debug(_('del_segments=%s'), body['delSegments'])
|
||||
if profile['sub_type'] == c_const.NETWORK_TYPE_OVERLAY:
|
||||
encap_profile = (network['name'] +
|
||||
encap_profile = (network['id'] +
|
||||
c_const.ENCAPSULATION_PROFILE_SUFFIX)
|
||||
encap_dict = {'name': encap_profile,
|
||||
'addMappings': (
|
||||
@ -817,7 +830,7 @@ class N1kvNeutronPluginV2(db_base_plugin_v2.NeutronDbPluginV2,
|
||||
del_segments))}
|
||||
n1kvclient.update_encapsulation_profile(context, encap_profile,
|
||||
encap_dict)
|
||||
n1kvclient.update_network_segment(network['name'], body)
|
||||
n1kvclient.update_network_segment(network['id'], body)
|
||||
|
||||
def _send_delete_network_request(self, context, network):
|
||||
"""
|
||||
@ -832,13 +845,13 @@ class N1kvNeutronPluginV2(db_base_plugin_v2.NeutronDbPluginV2,
|
||||
n1kvclient = n1kv_client.Client()
|
||||
session = context.session
|
||||
if network[providernet.NETWORK_TYPE] == c_const.NETWORK_TYPE_OVERLAY:
|
||||
name = network['name'] + c_const.BRIDGE_DOMAIN_SUFFIX
|
||||
name = network['id'] + c_const.BRIDGE_DOMAIN_SUFFIX
|
||||
n1kvclient.delete_bridge_domain(name)
|
||||
elif network[providernet.NETWORK_TYPE] == c_const.NETWORK_TYPE_TRUNK:
|
||||
profile = self.get_network_profile(
|
||||
context, network[n1kv_profile.PROFILE_ID])
|
||||
if profile['sub_type'] == c_const.NETWORK_TYPE_OVERLAY:
|
||||
profile_name = (network['name'] +
|
||||
profile_name = (network['id'] +
|
||||
c_const.ENCAPSULATION_PROFILE_SUFFIX)
|
||||
n1kvclient.delete_encapsulation_profile(profile_name)
|
||||
elif (network[providernet.NETWORK_TYPE] ==
|
||||
@ -859,7 +872,7 @@ class N1kvNeutronPluginV2(db_base_plugin_v2.NeutronDbPluginV2,
|
||||
profile_dict['delSegments'].append(mapping_dict)
|
||||
n1kvclient.update_encapsulation_profile(context, profile,
|
||||
profile_dict)
|
||||
n1kvclient.delete_network_segment(network['name'])
|
||||
n1kvclient.delete_network_segment(network['id'])
|
||||
|
||||
def _send_create_subnet_request(self, context, subnet):
|
||||
"""
|
||||
@ -869,11 +882,10 @@ class N1kvNeutronPluginV2(db_base_plugin_v2.NeutronDbPluginV2,
|
||||
:param subnet: subnet dictionary
|
||||
"""
|
||||
LOG.debug(_('_send_create_subnet_request: %s'), subnet['id'])
|
||||
network = self.get_network(context, subnet['network_id'])
|
||||
n1kvclient = n1kv_client.Client()
|
||||
n1kvclient.create_ip_pool(subnet)
|
||||
body = {'ipPoolName': subnet['name']}
|
||||
n1kvclient.update_network_segment(network['name'], body=body)
|
||||
body = {'ipPool': subnet['id']}
|
||||
n1kvclient.update_network_segment(subnet['network_id'], body=body)
|
||||
|
||||
def _send_delete_subnet_request(self, context, subnet):
|
||||
"""
|
||||
@ -883,11 +895,10 @@ class N1kvNeutronPluginV2(db_base_plugin_v2.NeutronDbPluginV2,
|
||||
:param subnet: subnet dictionary
|
||||
"""
|
||||
LOG.debug(_('_send_delete_subnet_request: %s'), subnet['name'])
|
||||
network = self.get_network(context, subnet['network_id'])
|
||||
body = {'ipPoolName': subnet['name'], 'deleteSubnet': True}
|
||||
body = {'ipPool': subnet['id'], 'deleteSubnet': True}
|
||||
n1kvclient = n1kv_client.Client()
|
||||
n1kvclient.update_network_segment(network['name'], body=body)
|
||||
n1kvclient.delete_ip_pool(subnet['name'])
|
||||
n1kvclient.update_network_segment(subnet['network_id'], body=body)
|
||||
n1kvclient.delete_ip_pool(subnet['id'])
|
||||
|
||||
def _send_create_port_request(self, context, port):
|
||||
"""
|
||||
@ -908,7 +919,6 @@ class N1kvNeutronPluginV2(db_base_plugin_v2.NeutronDbPluginV2,
|
||||
except cisco_exceptions.VMNetworkNotFound:
|
||||
policy_profile = n1kv_db_v2.get_policy_profile(
|
||||
context.session, port[n1kv_profile.PROFILE_ID])
|
||||
network = self.get_network(context, port['network_id'])
|
||||
vm_network_name = (c_const.VM_NETWORK_NAME_PREFIX +
|
||||
str(port[n1kv_profile.PROFILE_ID]) +
|
||||
"_" + str(port['network_id']))
|
||||
@ -921,8 +931,7 @@ class N1kvNeutronPluginV2(db_base_plugin_v2.NeutronDbPluginV2,
|
||||
n1kvclient = n1kv_client.Client()
|
||||
n1kvclient.create_vm_network(port,
|
||||
vm_network_name,
|
||||
policy_profile,
|
||||
network['name'])
|
||||
policy_profile)
|
||||
n1kvclient.create_n1kv_port(port, vm_network_name)
|
||||
else:
|
||||
vm_network_name = vm_network['name']
|
||||
@ -994,7 +1003,6 @@ class N1kvNeutronPluginV2(db_base_plugin_v2.NeutronDbPluginV2,
|
||||
(network_type, physical_network,
|
||||
segmentation_id) = self._process_provider_create(context,
|
||||
network['network'])
|
||||
self._add_dummy_profile_only_if_testing(network)
|
||||
profile_id = self._process_network_profile(context, network['network'])
|
||||
segment_pairs = None
|
||||
LOG.debug(_('Create network: profile_id=%s'), profile_id)
|
||||
@ -1219,8 +1227,6 @@ class N1kvNeutronPluginV2(db_base_plugin_v2.NeutronDbPluginV2,
|
||||
:param port: port dictionary
|
||||
:returns: port object
|
||||
"""
|
||||
self._add_dummy_profile_only_if_testing(port)
|
||||
|
||||
if ('device_id' in port['port'] and port['port']['device_owner'] in
|
||||
['network:dhcp', 'network:router_interface']):
|
||||
p_profile_name = c_conf.CISCO_N1K.network_node_policy_profile
|
||||
@ -1259,15 +1265,6 @@ class N1kvNeutronPluginV2(db_base_plugin_v2.NeutronDbPluginV2,
|
||||
LOG.debug(_("Created port: %s"), pt)
|
||||
return pt
|
||||
|
||||
def _add_dummy_profile_only_if_testing(self, obj):
|
||||
"""
|
||||
Method to be patched by the test_n1kv_plugin module to
|
||||
inject n1kv:profile_id into the network/port object, since the plugin
|
||||
tests for its existence. This method does not affect
|
||||
the plugin code in any way.
|
||||
"""
|
||||
pass
|
||||
|
||||
def update_port(self, context, id, port):
|
||||
"""
|
||||
Update port parameters.
|
||||
@ -1449,7 +1446,8 @@ class N1kvNeutronPluginV2(db_base_plugin_v2.NeutronDbPluginV2,
|
||||
n1kv_db_v2.sync_vxlan_allocations(context.session,
|
||||
self.vxlan_id_ranges)
|
||||
try:
|
||||
self._send_create_logical_network_request(_network_profile)
|
||||
self._send_create_logical_network_request(_network_profile,
|
||||
context.tenant_id)
|
||||
except(cisco_exceptions.VSMError,
|
||||
cisco_exceptions.VSMConnectionFailed):
|
||||
super(N1kvNeutronPluginV2, self).delete_network_profile(
|
||||
@ -1489,3 +1487,20 @@ class N1kvNeutronPluginV2(db_base_plugin_v2.NeutronDbPluginV2,
|
||||
n1kv_db_v2.delete_vxlan_allocations(context.session,
|
||||
self.delete_vxlan_ranges)
|
||||
self._send_delete_network_profile_request(_network_profile)
|
||||
|
||||
def update_network_profile(self, context, net_profile_id, network_profile):
|
||||
"""
|
||||
Update a network profile.
|
||||
|
||||
:param context: neutron api request context
|
||||
:param net_profile_id: UUID of the network profile to update
|
||||
:param network_profile: dictionary containing network profile object
|
||||
"""
|
||||
session = context.session
|
||||
with session.begin(subtransactions=True):
|
||||
net_p = (super(N1kvNeutronPluginV2, self).
|
||||
update_network_profile(context,
|
||||
net_profile_id,
|
||||
network_profile))
|
||||
self._send_update_network_profile_request(net_p)
|
||||
return net_p
|
||||
|
@ -18,16 +18,22 @@
|
||||
# @author: Abhishek Raut, Cisco Systems Inc.
|
||||
|
||||
from mock import patch
|
||||
import os
|
||||
from oslo.config import cfg
|
||||
|
||||
from neutron.api.v2 import attributes
|
||||
from neutron.common.test_lib import test_config
|
||||
from neutron import context
|
||||
import neutron.db.api as db
|
||||
from neutron.plugins.cisco.db import n1kv_db_v2
|
||||
from neutron.plugins.cisco.db import n1kv_models_v2
|
||||
from neutron.plugins.cisco.db import network_db_v2 as cdb
|
||||
from neutron.plugins.cisco import extensions
|
||||
from neutron.plugins.cisco.extensions import n1kv_profile
|
||||
from neutron.plugins.cisco.extensions import network_profile
|
||||
from neutron.plugins.cisco.n1kv import n1kv_client
|
||||
from neutron.plugins.cisco.n1kv import n1kv_neutron_plugin
|
||||
from neutron.tests import base
|
||||
from neutron.tests.unit import test_api_v2
|
||||
from neutron.tests.unit import test_db_plugin as test_plugin
|
||||
|
||||
|
||||
@ -51,41 +57,30 @@ class FakeResponse(object):
|
||||
return self.buffer
|
||||
|
||||
|
||||
def _fake_add_dummy_profile_for_test(self, obj):
|
||||
"""
|
||||
Replacement for a function in the N1KV neutron plugin module.
|
||||
|
||||
Since VSM is not available at the time of tests, we have no
|
||||
policy profiles. Hence we inject a dummy policy/network profile into the
|
||||
port/network object.
|
||||
"""
|
||||
dummy_profile_name = "dummy_profile"
|
||||
dummy_tenant_id = "test-tenant"
|
||||
db_session = db.get_session()
|
||||
if 'port' in obj:
|
||||
dummy_profile_id = "00000000-1111-1111-1111-000000000000"
|
||||
self._add_policy_profile(dummy_profile_name,
|
||||
dummy_profile_id,
|
||||
dummy_tenant_id)
|
||||
obj['port'][n1kv_profile.PROFILE_ID] = dummy_profile_id
|
||||
elif 'network' in obj:
|
||||
profile = {'name': 'dummy_profile',
|
||||
'segment_type': 'vlan',
|
||||
'physical_network': 'phsy1',
|
||||
'segment_range': '3968-4047'}
|
||||
self.network_vlan_ranges = {profile[
|
||||
'physical_network']: [(3968, 4047)]}
|
||||
n1kv_db_v2.sync_vlan_allocations(db_session, self.network_vlan_ranges)
|
||||
np = n1kv_db_v2.create_network_profile(db_session, profile)
|
||||
obj['network'][n1kv_profile.PROFILE_ID] = np.id
|
||||
|
||||
|
||||
def _fake_setup_vsm(self):
|
||||
"""Fake establish Communication with Cisco Nexus1000V VSM."""
|
||||
self.agent_vsm = True
|
||||
self._poll_policies(event_type="port_profile")
|
||||
|
||||
|
||||
class NetworkProfileTestExtensionManager(object):
|
||||
|
||||
def get_resources(self):
|
||||
# Add the resources to the global attribute map
|
||||
# This is done here as the setup process won't
|
||||
# initialize the main API router which extends
|
||||
# the global attribute map
|
||||
attributes.RESOURCE_ATTRIBUTE_MAP.update(
|
||||
network_profile.RESOURCE_ATTRIBUTE_MAP)
|
||||
return network_profile.Network_profile.get_resources()
|
||||
|
||||
def get_actions(self):
|
||||
return []
|
||||
|
||||
def get_request_extensions(self):
|
||||
return []
|
||||
|
||||
|
||||
class N1kvPluginTestCase(test_plugin.NeutronDbPluginV2TestCase):
|
||||
|
||||
_plugin_name = ('neutron.plugins.cisco.n1kv.'
|
||||
@ -96,31 +91,38 @@ class N1kvPluginTestCase(test_plugin.NeutronDbPluginV2TestCase):
|
||||
DEFAULT_RESP_BODY = ""
|
||||
DEFAULT_RESP_CODE = 200
|
||||
DEFAULT_CONTENT_TYPE = ""
|
||||
fmt = "json"
|
||||
|
||||
def _make_test_policy_profile(self, id):
|
||||
"""Create a policy profile record for testing purpose."""
|
||||
profile = {'id': id,
|
||||
'name': 'TestGrizzlyPP'}
|
||||
profile_obj = n1kv_db_v2.create_policy_profile(profile)
|
||||
return profile_obj
|
||||
def _make_test_policy_profile(self, name='service_profile'):
|
||||
"""
|
||||
Create a policy profile record for testing purpose.
|
||||
|
||||
def _make_test_profile(self):
|
||||
"""Create a profile record for testing purposes."""
|
||||
alloc_obj = n1kv_models_v2.N1kvVlanAllocation(physical_network='foo',
|
||||
vlan_id=123)
|
||||
alloc_obj.allocated = False
|
||||
segment_range = "100-900"
|
||||
segment_type = 'vlan'
|
||||
physical_network = 'phys1'
|
||||
profile_obj = n1kv_models_v2.NetworkProfile(
|
||||
name="test_np",
|
||||
segment_type=segment_type,
|
||||
segment_range=segment_range,
|
||||
physical_network=physical_network)
|
||||
session = db.get_session()
|
||||
session.add(profile_obj)
|
||||
session.flush()
|
||||
return profile_obj
|
||||
:param name: string representing the name of the policy profile to
|
||||
create. Default argument value chosen to correspond to the
|
||||
default name specified in config.py file.
|
||||
"""
|
||||
uuid = test_api_v2._uuid()
|
||||
profile = {'id': uuid,
|
||||
'name': name}
|
||||
return n1kv_db_v2.create_policy_profile(profile)
|
||||
|
||||
def _make_test_profile(self, name='default_network_profile'):
|
||||
"""
|
||||
Create a profile record for testing purposes.
|
||||
|
||||
:param name: string representing the name of the network profile to
|
||||
create. Default argument value chosen to correspond to the
|
||||
default name specified in config.py file.
|
||||
"""
|
||||
db_session = db.get_session()
|
||||
profile = {'name': name,
|
||||
'segment_type': 'vlan',
|
||||
'physical_network': 'phsy1',
|
||||
'segment_range': '3968-4047'}
|
||||
self.network_vlan_ranges = {profile[
|
||||
'physical_network']: [(3968, 4047)]}
|
||||
n1kv_db_v2.sync_vlan_allocations(db_session, self.network_vlan_ranges)
|
||||
return n1kv_db_v2.create_network_profile(db_session, profile)
|
||||
|
||||
def setUp(self):
|
||||
"""
|
||||
@ -199,24 +201,39 @@ class N1kvPluginTestCase(test_plugin.NeutronDbPluginV2TestCase):
|
||||
fake_get_cred_name.return_value = {"user_name": "admin",
|
||||
"password": "admin_password"}
|
||||
|
||||
# Patch a dummy profile creation into the N1K plugin code. The original
|
||||
# function in the plugin is a noop for production, but during test, we
|
||||
# need it to return a dummy network profile.
|
||||
(n1kv_neutron_plugin.N1kvNeutronPluginV2.
|
||||
_add_dummy_profile_only_if_testing) = _fake_add_dummy_profile_for_test
|
||||
|
||||
n1kv_neutron_plugin.N1kvNeutronPluginV2._setup_vsm = _fake_setup_vsm
|
||||
|
||||
test_config['plugin_name_v2'] = self._plugin_name
|
||||
cfg.CONF.set_override('api_extensions_path',
|
||||
os.path.dirname(extensions.__file__))
|
||||
self.addCleanup(cfg.CONF.reset)
|
||||
ext_mgr = NetworkProfileTestExtensionManager()
|
||||
test_config['extension_manager'] = ext_mgr
|
||||
self.addCleanup(self.restore_test_config)
|
||||
|
||||
# Save the original RESOURCE_ATTRIBUTE_MAP
|
||||
self.saved_attr_map = {}
|
||||
for resource, attrs in attributes.RESOURCE_ATTRIBUTE_MAP.items():
|
||||
self.saved_attr_map[resource] = attrs.copy()
|
||||
# Update the RESOURCE_ATTRIBUTE_MAP with n1kv specific extended attrs.
|
||||
attributes.RESOURCE_ATTRIBUTE_MAP["networks"].update(
|
||||
n1kv_profile.EXTENDED_ATTRIBUTES_2_0["networks"])
|
||||
attributes.RESOURCE_ATTRIBUTE_MAP["ports"].update(
|
||||
n1kv_profile.EXTENDED_ATTRIBUTES_2_0["ports"])
|
||||
self.addCleanup(self.restore_resource_attribute_map)
|
||||
self.addCleanup(db.clear_db)
|
||||
super(N1kvPluginTestCase, self).setUp(self._plugin_name)
|
||||
# Create some of the database entries that we require.
|
||||
profile_obj = self._make_test_profile()
|
||||
policy_profile_obj = (self._make_test_policy_profile(
|
||||
'41548d21-7f89-4da0-9131-3d4fd4e8BBB8'))
|
||||
# Additional args for create_network(), create_port(), etc.
|
||||
self.more_args = {
|
||||
"network": {"n1kv:profile_id": profile_obj.id},
|
||||
"port": {"n1kv:profile_id": policy_profile_obj.id}
|
||||
}
|
||||
self._make_test_profile()
|
||||
self._make_test_policy_profile()
|
||||
|
||||
def restore_resource_attribute_map(self):
|
||||
# Restore the original RESOURCE_ATTRIBUTE_MAP
|
||||
attributes.RESOURCE_ATTRIBUTE_MAP = self.saved_attr_map
|
||||
|
||||
def restore_test_config(self):
|
||||
# Restore the original test_config
|
||||
del test_config['plugin_name_v2']
|
||||
|
||||
def test_plugin(self):
|
||||
self._make_network('json',
|
||||
@ -233,6 +250,56 @@ class N1kvPluginTestCase(test_plugin.NeutronDbPluginV2TestCase):
|
||||
self.assertIn('tenant_id', body['networks'][0])
|
||||
|
||||
|
||||
class TestN1kvNetworkProfiles(N1kvPluginTestCase):
|
||||
def _prepare_net_profile_data(self, segment_type):
|
||||
netp = {'network_profile': {'name': 'netp1',
|
||||
'segment_type': segment_type,
|
||||
'tenant_id': self.tenant_id}}
|
||||
if segment_type == 'vlan':
|
||||
netp['network_profile']['segment_range'] = '100-200'
|
||||
netp['network_profile']['physical_network'] = 'phys1'
|
||||
elif segment_type == 'overlay':
|
||||
netp['network_profile']['segment_range'] = '10000-10010'
|
||||
netp['network_profile']['sub_type'] = 'enhanced'
|
||||
return netp
|
||||
|
||||
def test_create_network_profile_plugin(self):
|
||||
data = self._prepare_net_profile_data('vlan')
|
||||
net_p_req = self.new_create_request('network_profiles', data)
|
||||
res = net_p_req.get_response(self.ext_api)
|
||||
self.assertEqual(res.status_int, 201)
|
||||
|
||||
def test_update_network_profile_physical_network_fail(self):
|
||||
net_p = self._make_test_profile(name='netp1')
|
||||
data = {'network_profile': {'physical_network': 'some-phys-net'}}
|
||||
net_p_req = self.new_update_request('network_profiles',
|
||||
data,
|
||||
net_p['id'])
|
||||
res = net_p_req.get_response(self.ext_api)
|
||||
self.assertEqual(res.status_int, 400)
|
||||
|
||||
def test_update_network_profile_segment_type_fail(self):
|
||||
net_p = self._make_test_profile(name='netp1')
|
||||
data = {'network_profile': {'segment_type': 'overlay'}}
|
||||
net_p_req = self.new_update_request('network_profiles',
|
||||
data,
|
||||
net_p['id'])
|
||||
res = net_p_req.get_response(self.ext_api)
|
||||
self.assertEqual(res.status_int, 400)
|
||||
|
||||
def test_update_network_profile_sub_type_fail(self):
|
||||
net_p_dict = self._prepare_net_profile_data('overlay')
|
||||
net_p_req = self.new_create_request('network_profiles', net_p_dict)
|
||||
net_p = self.deserialize(self.fmt,
|
||||
net_p_req.get_response(self.ext_api))
|
||||
data = {'network_profile': {'sub_type': 'vlan'}}
|
||||
update_req = self.new_update_request('network_profiles',
|
||||
data,
|
||||
net_p['network_profile']['id'])
|
||||
update_res = update_req.get_response(self.ext_api)
|
||||
self.assertEqual(update_res.status_int, 400)
|
||||
|
||||
|
||||
class TestN1kvBasicGet(test_plugin.TestBasicGet,
|
||||
N1kvPluginTestCase):
|
||||
|
||||
@ -248,61 +315,76 @@ class TestN1kvHTTPResponse(test_plugin.TestV2HTTPResponse,
|
||||
class TestN1kvPorts(test_plugin.TestPortsV2,
|
||||
N1kvPluginTestCase):
|
||||
|
||||
def _make_other_tenant_profile(self):
|
||||
"""Underlying test uses other tenant Id for tests."""
|
||||
profile_obj = self._make_test_profile()
|
||||
policy_profile_obj = self._make_test_policy_profile(
|
||||
'41548d21-7f89-4da0-9131-3d4fd4e8BBB9')
|
||||
self.more_args = {
|
||||
"network": {"n1kv:profile_id": profile_obj.id},
|
||||
"port": {"n1kv:profile_id": policy_profile_obj.id}
|
||||
}
|
||||
def test_create_port_with_default_n1kv_profile_id(self):
|
||||
"""Test port create without passing policy profile id."""
|
||||
with self.port() as port:
|
||||
db_session = db.get_session()
|
||||
pp = n1kv_db_v2.get_policy_profile(
|
||||
db_session, port['port'][n1kv_profile.PROFILE_ID])
|
||||
self.assertEqual(pp['name'], 'service_profile')
|
||||
|
||||
def test_create_port_public_network(self):
|
||||
# The underlying test function needs a profile for a different tenant.
|
||||
self._make_other_tenant_profile()
|
||||
super(TestN1kvPorts, self).test_create_port_public_network()
|
||||
def test_create_port_with_n1kv_profile_id(self):
|
||||
"""Test port create with policy profile id."""
|
||||
profile_obj = self._make_test_policy_profile(name='test_profile')
|
||||
with self.network() as network:
|
||||
data = {'port': {n1kv_profile.PROFILE_ID: profile_obj.id,
|
||||
'tenant_id': self.tenant_id,
|
||||
'network_id': network['network']['id']}}
|
||||
port_req = self.new_create_request('ports', data)
|
||||
port = self.deserialize(self.fmt,
|
||||
port_req.get_response(self.api))
|
||||
self.assertEqual(port['port'][n1kv_profile.PROFILE_ID],
|
||||
profile_obj.id)
|
||||
self._delete('ports', port['port']['id'])
|
||||
|
||||
def test_create_port_public_network_with_ip(self):
|
||||
# The underlying test function needs a profile for a different tenant.
|
||||
self._make_other_tenant_profile()
|
||||
super(TestN1kvPorts, self).test_create_port_public_network_with_ip()
|
||||
|
||||
def test_create_ports_bulk_emulated(self):
|
||||
# The underlying test function needs a profile for a different tenant.
|
||||
self._make_other_tenant_profile()
|
||||
super(TestN1kvPorts,
|
||||
self).test_create_ports_bulk_emulated()
|
||||
|
||||
def test_create_ports_bulk_emulated_plugin_failure(self):
|
||||
# The underlying test function needs a profile for a different tenant.
|
||||
self._make_other_tenant_profile()
|
||||
super(TestN1kvPorts,
|
||||
self).test_create_ports_bulk_emulated_plugin_failure()
|
||||
|
||||
def test_delete_port_public_network(self):
|
||||
self._make_other_tenant_profile()
|
||||
super(TestN1kvPorts, self).test_delete_port_public_network()
|
||||
def test_update_port_with_n1kv_profile_id(self):
|
||||
"""Test port update failure while updating policy profile id."""
|
||||
with self.port() as port:
|
||||
data = {'port': {n1kv_profile.PROFILE_ID: 'some-profile-uuid'}}
|
||||
port_req = self.new_update_request('ports',
|
||||
data,
|
||||
port['port']['id'])
|
||||
res = port_req.get_response(self.api)
|
||||
# Port update should fail to update policy profile id.
|
||||
self.assertEqual(res.status_int, 400)
|
||||
|
||||
|
||||
class TestN1kvNetworks(test_plugin.TestNetworksV2,
|
||||
N1kvPluginTestCase):
|
||||
|
||||
_default_tenant = "somebody_else" # Tenant-id determined by underlying
|
||||
# DB-plugin test cases. Need to use this
|
||||
# one for profile creation
|
||||
def _prepare_net_data(self, net_profile_id):
|
||||
return {'network': {'name': 'net1',
|
||||
n1kv_profile.PROFILE_ID: net_profile_id,
|
||||
'tenant_id': self.tenant_id}}
|
||||
|
||||
def test_update_network_set_not_shared_single_tenant(self):
|
||||
# The underlying test function needs a profile for a different tenant.
|
||||
profile_obj = self._make_test_profile()
|
||||
policy_profile_obj = self._make_test_policy_profile(
|
||||
'41548d21-7f89-4da0-9131-3d4fd4e8BBB9')
|
||||
self.more_args = {
|
||||
"network": {"n1kv:profile_id": profile_obj.id},
|
||||
"port": {"n1kv:profile_id": policy_profile_obj.id}
|
||||
}
|
||||
super(TestN1kvNetworks,
|
||||
self).test_update_network_set_not_shared_single_tenant()
|
||||
def test_create_network_with_default_n1kv_profile_id(self):
|
||||
"""Test network create without passing network profile id."""
|
||||
with self.network() as network:
|
||||
db_session = db.get_session()
|
||||
np = n1kv_db_v2.get_network_profile(
|
||||
db_session, network['network'][n1kv_profile.PROFILE_ID])
|
||||
self.assertEqual(np['name'], 'default_network_profile')
|
||||
|
||||
def test_create_network_with_n1kv_profile_id(self):
|
||||
"""Test network create with network profile id."""
|
||||
profile_obj = self._make_test_profile(name='test_profile')
|
||||
data = self._prepare_net_data(profile_obj.id)
|
||||
network_req = self.new_create_request('networks', data)
|
||||
network = self.deserialize(self.fmt,
|
||||
network_req.get_response(self.api))
|
||||
self.assertEqual(network['network'][n1kv_profile.PROFILE_ID],
|
||||
profile_obj.id)
|
||||
|
||||
def test_update_network_with_n1kv_profile_id(self):
|
||||
"""Test network update failure while updating network profile id."""
|
||||
with self.network() as network:
|
||||
data = {'network': {n1kv_profile.PROFILE_ID: 'some-profile-uuid'}}
|
||||
network_req = self.new_update_request('networks',
|
||||
data,
|
||||
network['network']['id'])
|
||||
res = network_req.get_response(self.api)
|
||||
# Network update should fail to update network profile id.
|
||||
self.assertEqual(res.status_int, 400)
|
||||
|
||||
|
||||
class TestN1kvNonDbTest(base.BaseTestCase):
|
||||
|
Loading…
Reference in New Issue
Block a user