|
33 | 33 | import zaza.openstack.charm_tests.test_utils as test_utils
|
34 | 34 | import zaza.openstack.configure.guest as guest
|
35 | 35 | import zaza.openstack.utilities.openstack as openstack_utils
|
| 36 | +import zaza.utilities.machine_os |
36 | 37 |
|
37 | 38 |
|
38 | 39 | class NeutronPluginApiSharedTests(test_utils.OpenStackBaseTest):
|
@@ -835,6 +836,10 @@ def setUpClass(cls):
|
835 | 836 | # the external provider network
|
836 | 837 | cls.attach_to_external_network = False
|
837 | 838 |
|
| 839 | + # Override this if you want your test to launch instances with a |
| 840 | + # specific flavor |
| 841 | + cls.instance_flavor = None |
| 842 | + |
838 | 843 | @tenacity.retry(wait=tenacity.wait_exponential(multiplier=1, max=60),
|
839 | 844 | reraise=True, stop=tenacity.stop_after_attempt(8))
|
840 | 845 | def validate_instance_can_reach_other(self,
|
@@ -1112,13 +1117,177 @@ class name + run_tearDown key under the `tests_options` key in
|
1112 | 1117 | instance_1, instance_2 = self.retrieve_guests()
|
1113 | 1118 | if not all([instance_1, instance_2]):
|
1114 | 1119 | self.launch_guests(
|
1115 |
| - attach_to_external_network=self.attach_to_external_network) |
| 1120 | + attach_to_external_network=self.attach_to_external_network, |
| 1121 | + flavor_name=self.instance_flavor) |
1116 | 1122 | instance_1, instance_2 = self.retrieve_guests()
|
1117 | 1123 | self.check_connectivity(instance_1, instance_2)
|
1118 | 1124 | self.run_resource_cleanup = self.get_my_tests_options(
|
1119 | 1125 | 'run_resource_cleanup', True)
|
1120 | 1126 |
|
1121 | 1127 |
|
| 1128 | +class DPDKNeutronNetworkingTest(NeutronNetworkingTest): |
| 1129 | + """Ensure that openstack instances have valid networking with DPDK.""" |
| 1130 | + |
| 1131 | + @classmethod |
| 1132 | + def setUpClass(cls): |
| 1133 | + """Run class setup for running Neutron API Networking tests.""" |
| 1134 | + super(DPDKNeutronNetworkingTest, cls).setUpClass() |
| 1135 | + |
| 1136 | + # At this point in time the charms do not support configuring overlay |
| 1137 | + # networks with DPDK. To perform end to end validation we need to |
| 1138 | + # attach instances directly to the provider network and subsequently |
| 1139 | + # DHCP needs to be enabled on that network. |
| 1140 | + # |
| 1141 | + # Note that for instances wired with DPDK the DHCP request/response is |
| 1142 | + # handled as private communication between the ovn-controller and the |
| 1143 | + # instance, and as such there is no risk of rogue DHCP replies escaping |
| 1144 | + # to the surrounding network. |
| 1145 | + cls.attach_to_external_network = True |
| 1146 | + cls.instance_flavor = 'hugepages' |
| 1147 | + cls.external_subnet = cls.neutron_client.find_resource( |
| 1148 | + 'subnet', |
| 1149 | + neutron_setup.OVERCLOUD_NETWORK_CONFIG['external_subnet_name']) |
| 1150 | + if ('dhcp_enabled' not in cls.external_subnet or |
| 1151 | + not cls.external_subnet['dhcp_enabled']): |
| 1152 | + logging.info('Enabling DHCP on subnet {}' |
| 1153 | + .format(cls.external_subnet['name'])) |
| 1154 | + openstack_utils.update_subnet_dhcp( |
| 1155 | + cls.neutron_client, cls.external_subnet, True) |
| 1156 | + |
| 1157 | + cls.nr_1g_hugepages = 4 |
| 1158 | + # Prepare hypervisor units for the test |
| 1159 | + for unit in zaza.model.get_units( |
| 1160 | + zaza.utilities.machine_os.get_hv_application(), |
| 1161 | + model_name=cls.model_name): |
| 1162 | + if not zaza.utilities.machine_os.is_vm(unit.name, |
| 1163 | + model_name=cls.model_name): |
| 1164 | + logging.info('Unit {} is a physical machine, assuming ' |
| 1165 | + 'hugepages and IOMMU configuration already ' |
| 1166 | + 'performed through kernel command line.') |
| 1167 | + continue |
| 1168 | + logging.info('Checking CPU topology on {}'.format(unit.name)) |
| 1169 | + cls._assert_unit_cpu_topology(cls, unit, model_name=cls.model_name) |
| 1170 | + logging.info('Enabling hugepages on {}'.format(unit.name)) |
| 1171 | + zaza.utilities.machine_os.enable_hugepages( |
| 1172 | + unit, cls.nr_1g_hugepages, model_name=cls.model_name) |
| 1173 | + logging.info('Enabling unsafe VFIO NOIOMMU mode on {}' |
| 1174 | + .format(unit.name)) |
| 1175 | + zaza.utilities.machine_os.enable_vfio_unsafe_noiommu_mode( |
| 1176 | + unit, model_name=cls.model_name) |
| 1177 | + |
| 1178 | + def _assert_unit_cpu_topology(self, unit, model_name=None): |
| 1179 | + r"""Assert unit under test CPU topology. |
| 1180 | +
|
| 1181 | + When using OpenStack as CI substrate: |
| 1182 | +
|
| 1183 | + By default, when instance NUMA placement is not specified, |
| 1184 | + a topology of N sockets, each with one core and one thread, |
| 1185 | + is used for an instance, where N corresponds to the number of |
| 1186 | + instance vCPUs requested. |
| 1187 | +
|
| 1188 | + In this context a socket is a physical socket on the motherboard |
| 1189 | + where a CPU is connected. |
| 1190 | +
|
| 1191 | + The DPDK Environment Abstraction Layer (EAL) allocates memory per |
| 1192 | + CPU socket, so we want the CPU topology inside the instance to |
| 1193 | + mimic something we would be likely to find in the real world and |
| 1194 | + at the same time not make the test too heavy. |
| 1195 | +
|
| 1196 | + The charm default is to have Open vSwitch allocate 1GB RAM per |
| 1197 | + CPU socket. |
| 1198 | +
|
| 1199 | + The following command would set the apropriate CPU topology for a |
| 1200 | + 4 VCPU flavor: |
| 1201 | +
|
| 1202 | + openstack flavor set m1.large \ |
| 1203 | + --property hw:cpu_sockets=2 \ |
| 1204 | + --property hw:cpu_cores=2 \ |
| 1205 | + --property hw:cpu_threads=2 |
| 1206 | + """ |
| 1207 | + # Get number of sockets |
| 1208 | + cmd = 'lscpu -p|grep -v ^#|cut -f3 -d,|sort|uniq|wc -l' |
| 1209 | + sockets = int(zaza.utilities.juju.remote_run( |
| 1210 | + unit.name, cmd, model_name=model_name, fatal=True).rstrip()) |
| 1211 | + |
| 1212 | + # Get total memory |
| 1213 | + cmd = 'cat /proc/meminfo |grep ^MemTotal' |
| 1214 | + _, meminfo_value, _ = zaza.utilities.juju.remote_run( |
| 1215 | + unit.name, |
| 1216 | + cmd, |
| 1217 | + model_name=model_name, |
| 1218 | + fatal=True).rstrip().split() |
| 1219 | + mbtotal = int(meminfo_value) * 1024 / 1000 / 1000 |
| 1220 | + mbtotalhugepages = self.nr_1g_hugepages * 1024 |
| 1221 | + |
| 1222 | + # headroom for operating system and daemons in instance |
| 1223 | + mbsystemheadroom = 2048 |
| 1224 | + # memory to be consumed by the nested instance |
| 1225 | + mbinstance = 1024 |
| 1226 | + |
| 1227 | + # the amount of hugepage memory OVS / DPDK EAL will allocate |
| 1228 | + mbovshugepages = sockets * 1024 |
| 1229 | + # the amount of hugepage memory available for nested instance |
| 1230 | + mbfreehugepages = mbtotalhugepages - mbovshugepages |
| 1231 | + |
| 1232 | + assert (mbtotal - mbtotalhugepages >= mbsystemheadroom and |
| 1233 | + mbfreehugepages >= mbinstance), ( |
| 1234 | + 'Unit {} is not suitable for test, please adjust instance ' |
| 1235 | + 'type CPU topology or provide suitable physical machine. ' |
| 1236 | + 'CPU Sockets: {} ' |
| 1237 | + 'Available memory: {} MB ' |
| 1238 | + 'Details:\n{}' |
| 1239 | + .format(unit.name, |
| 1240 | + sockets, |
| 1241 | + mbtotal, |
| 1242 | + self._assert_unit_cpu_topology.__doc__)) |
| 1243 | + |
| 1244 | + def test_instances_have_networking(self): |
| 1245 | + """Enable DPDK then Validate North/South and East/West networking.""" |
| 1246 | + with self.config_change( |
| 1247 | + { |
| 1248 | + 'enable-dpdk': False, |
| 1249 | + 'dpdk-driver': '', |
| 1250 | + }, |
| 1251 | + { |
| 1252 | + 'enable-dpdk': True, |
| 1253 | + 'dpdk-driver': 'vfio-pci', |
| 1254 | + }, |
| 1255 | + application_name='ovn-chassis'): |
| 1256 | + super().test_instances_have_networking() |
| 1257 | + self.run_resource_cleanup = self.get_my_tests_options( |
| 1258 | + 'run_resource_cleanup', True) |
| 1259 | + |
| 1260 | + def resource_cleanup(self): |
| 1261 | + """Extend to also revert VFIO NOIOMMU mode on units under test.""" |
| 1262 | + super().resource_cleanup() |
| 1263 | + if not self.run_resource_cleanup: |
| 1264 | + return |
| 1265 | + |
| 1266 | + if ('dhcp_enabled' not in self.external_subnet or |
| 1267 | + not self.external_subnet['dhcp_enabled']): |
| 1268 | + logging.info('Disabling DHCP on subnet {}' |
| 1269 | + .format(self.external_subnet['name'])) |
| 1270 | + openstack_utils.update_subnet_dhcp( |
| 1271 | + self.neutron_client, self.external_subnet, False) |
| 1272 | + |
| 1273 | + for unit in zaza.model.get_units( |
| 1274 | + zaza.utilities.machine_os.get_hv_application(), |
| 1275 | + model_name=self.model_name): |
| 1276 | + if not zaza.utilities.machine_os.is_vm(unit.name, |
| 1277 | + model_name=self.model_name): |
| 1278 | + logging.info('Unit {} is a physical machine, assuming ' |
| 1279 | + 'hugepages and IOMMU configuration already ' |
| 1280 | + 'performed through kernel command line.') |
| 1281 | + continue |
| 1282 | + logging.info('Disabling hugepages on {}'.format(unit.name)) |
| 1283 | + zaza.utilities.machine_os.disable_hugepages( |
| 1284 | + unit, model_name=self.model_name) |
| 1285 | + logging.info('Disabling unsafe VFIO NOIOMMU mode on {}' |
| 1286 | + .format(unit.name)) |
| 1287 | + zaza.utilities.machine_os.disable_vfio_unsafe_noiommu_mode( |
| 1288 | + unit, model_name=self.model_name) |
| 1289 | + |
| 1290 | + |
1122 | 1291 | class NeutronNetworkingVRRPTests(NeutronNetworkingBase):
|
1123 | 1292 | """Check networking when gateways are restarted."""
|
1124 | 1293 |
|
|
0 commit comments