@@ -114,7 +114,7 @@ def _get_connection(
114114 def start_compute (
115115 self , hostname = 'compute1' , host_info = None , pci_info = None ,
116116 mdev_info = None , vdpa_info = None , libvirt_version = None ,
117- qemu_version = None ,
117+ qemu_version = None , cell_name = None , connection = None
118118 ):
119119 """Start a compute service.
120120
@@ -124,16 +124,35 @@ def start_compute(
124124 :param host_info: A fakelibvirt.HostInfo object for the host. Defaults
125125 to a HostInfo with 2 NUMA nodes, 2 cores per node, 2 threads per
126126 core, and 16GB of RAM.
127+ :param connection: A fake libvirt connection. You should not provide it
128+ directly. However it is used by restart_compute_service to
129+ implement restart without loosing the hypervisor state.
127130 :returns: The hostname of the created service, which can be used to
128131 lookup the created service and UUID of the assocaited resource
129132 provider.
130133 """
134+ if connection and (
135+ host_info or
136+ pci_info or
137+ mdev_info or
138+ vdpa_info or
139+ libvirt_version or
140+ qemu_version
141+ ):
142+ raise ValueError (
143+ "Either an existing connection instance can be provided or a "
144+ "list of parameters for a new connection"
145+ )
131146
132147 def _start_compute (hostname , host_info ):
133- fake_connection = self ._get_connection (
134- host_info , pci_info , mdev_info , vdpa_info , libvirt_version ,
135- qemu_version , hostname ,
136- )
148+ if connection :
149+ fake_connection = connection
150+ else :
151+ fake_connection = self ._get_connection (
152+ host_info , pci_info , mdev_info , vdpa_info , libvirt_version ,
153+ qemu_version , hostname ,
154+ )
155+
137156 # If the compute is configured with PCI devices then we need to
138157 # make sure that the stubs around sysfs has the MAC address
139158 # information for the PCI PF devices
@@ -144,7 +163,8 @@ def _start_compute(hostname, host_info):
144163 # actually start the service.
145164 orig_con = self .mock_conn .return_value
146165 self .mock_conn .return_value = fake_connection
147- compute = self .start_service ('compute' , host = hostname )
166+ compute = self .start_service (
167+ 'compute' , host = hostname , cell_name = cell_name )
148168 # Once that's done, we need to tweak the compute "service" to
149169 # make sure it returns unique objects.
150170 compute .driver ._host .get_connection = lambda : fake_connection
@@ -165,6 +185,74 @@ def _start_compute(hostname, host_info):
165185
166186 return hostname
167187
188+ def restart_compute_service (
189+ self ,
190+ hostname ,
191+ host_info = None ,
192+ pci_info = None ,
193+ mdev_info = None ,
194+ vdpa_info = None ,
195+ libvirt_version = None ,
196+ qemu_version = None ,
197+ keep_hypervisor_state = True ,
198+ ):
199+ """Stops the service and starts a new one to have realistic restart
200+
201+ :param hostname: the hostname of the nova-compute service to be
202+ restarted
203+ :param keep_hypervisor_state: If True then we reuse the fake connection
204+ from the existing driver. If False a new connection will be created
205+ based on the other parameters provided
206+ """
207+ # We are intentionally not calling super() here. Nova's base test class
208+ # defines starting and restarting compute service with a very
209+ # different signatures and also those calls are cannot be made aware of
210+ # the intricacies of the libvirt fixture. So we simply hide that
211+ # implementation.
212+
213+ if keep_hypervisor_state and (
214+ host_info or
215+ pci_info or
216+ mdev_info or
217+ vdpa_info or
218+ libvirt_version or
219+ qemu_version
220+ ):
221+ raise ValueError (
222+ "Either keep_hypervisor_state=True or a list of libvirt "
223+ "parameters can be provided but not both"
224+ )
225+
226+ compute = self .computes .pop (hostname )
227+ self .compute_rp_uuids .pop (hostname )
228+
229+ # NOTE(gibi): The service interface cannot be used to simulate a real
230+ # service restart as the manager object will not be recreated after a
231+ # service.stop() and service.start() therefore the manager state will
232+ # survive. For example the resource tracker will not be recreated after
233+ # a stop start. The service.kill() call cannot help as it deletes
234+ # the service from the DB which is unrealistic and causes that some
235+ # operation that refers to the killed host (e.g. evacuate) fails.
236+ # So this helper method will stop the original service and then starts
237+ # a brand new compute service for the same host and node. This way
238+ # a new ComputeManager instance will be created and initialized during
239+ # the service startup.
240+ compute .stop ()
241+
242+ # this service was running previously, so we have to make sure that
243+ # we restart it in the same cell
244+ cell_name = self .host_mappings [compute .host ].cell_mapping .name
245+
246+ old_connection = compute .manager .driver ._get_connection ()
247+
248+ self .start_compute (
249+ hostname , host_info , pci_info , mdev_info , vdpa_info ,
250+ libvirt_version , qemu_version , cell_name ,
251+ old_connection if keep_hypervisor_state else None
252+ )
253+
254+ return self .computes [hostname ]
255+
168256
169257class LibvirtMigrationMixin (object ):
170258 """A simple mixin to facilliate successful libvirt live migrations
0 commit comments