From 17b9de140f61ec6ddb1f18aa6db0594e990e282b Mon Sep 17 00:00:00 2001 From: Bo Chen Date: Tue, 25 Feb 2020 22:49:37 -0800 Subject: [PATCH] clh: enable CPU hotplug With the HTTP API 'vm.resize()', the CPU hotplug with CLH is much simpler comparing with QEMU. This is because we don't need to distinguish adding from removing CPUs. Fixes: #2495 Depends-on: github.com/kata-containers/packaging#968 Depends-on: github.com/kata-containers/tests#2364 Signed-off-by: Bo Chen --- virtcontainers/clh.go | 44 ++++++++++++++++++++++++++++++++++++-- virtcontainers/clh_test.go | 5 +++++ 2 files changed, 47 insertions(+), 2 deletions(-) diff --git a/virtcontainers/clh.go b/virtcontainers/clh.go index 3bc63068f0..e5ec0a59ff 100644 --- a/virtcontainers/clh.go +++ b/virtcontainers/clh.go @@ -76,6 +76,7 @@ type clhClient interface { // No lint: golint suggest to rename to VMInfoGet. VmInfoGet(ctx context.Context) (chclient.VmInfo, *http.Response, error) //nolint:golint BootVM(ctx context.Context) (*http.Response, error) + VmResizePut(ctx context.Context, vmResize chclient.VmResize) (*http.Response, error) } type CloudHypervisorVersion struct { @@ -391,8 +392,47 @@ func (clh *cloudHypervisor) resizeMemory(reqMemMB uint32, memoryBlockSizeMB uint } func (clh *cloudHypervisor) resizeVCPUs(reqVCPUs uint32) (currentVCPUs uint32, newVCPUs uint32, err error) { - clh.Logger().WithField("function", "resizeVCPUs").Warn("not supported") - return 0, 0, nil + cl := clh.client() + + // Retrieve the number of current vCPUs via HTTP API + ctx, cancel := context.WithTimeout(context.Background(), clhAPITimeout*time.Second) + info, _, err := cl.VmInfoGet(ctx) + if err != nil { + clh.Logger().WithField("function", "resizeVCPUs").WithError(openAPIClientError(err)).Info("[clh] VmInfoGet failed") + return 0, 0, openAPIClientError(err) + } + // Reset the timer after the first HTTP API call + cancel() + + currentVCPUs = uint32(info.Config.Cpus.BootVcpus) + newVCPUs = currentVCPUs + + // Sanity check + if reqVCPUs == 0 { + clh.Logger().WithField("function", "resizeVCPUs").Debugf("Cannot resize vCPU to 0") + return currentVCPUs, newVCPUs, fmt.Errorf("Cannot resize vCPU to 0") + } + if reqVCPUs > clh.config.DefaultMaxVCPUs || reqVCPUs > uint32(info.Config.Cpus.MaxVcpus) { + clh.Logger().WithFields(log.Fields{ + "function": "resizeVCPUs", + "reqVCPUs": reqVCPUs, + "configDefaultMaxVCPUs": clh.config.DefaultMaxVCPUs, + "clhMaxVCPUs": info.Config.Cpus.MaxVcpus, + }).Debug("exceeding the maxVCPUs") + return currentVCPUs, newVCPUs, fmt.Errorf("Cannot resize vCPU to %d: exceeding the maximum amount of vCPUs (%d or %d)", + reqVCPUs, clh.config.DefaultMaxVCPUs, info.Config.Cpus.MaxVcpus) + } + + // Resize (hot-plug) vCPUs via HTTP API + ctx, cancel = context.WithTimeout(context.Background(), clhAPITimeout*time.Second) + defer cancel() + if _, err = cl.VmResizePut(ctx, chclient.VmResize{DesiredVcpus: int32(reqVCPUs)}); err != nil { + return currentVCPUs, newVCPUs, errors.Wrap(err, "[clh] VmResizePut failed") + } + + newVCPUs = reqVCPUs + + return currentVCPUs, newVCPUs, nil } func (clh *cloudHypervisor) cleanup() error { diff --git a/virtcontainers/clh_test.go b/virtcontainers/clh_test.go index 2299a65c62..c3f5c6589a 100644 --- a/virtcontainers/clh_test.go +++ b/virtcontainers/clh_test.go @@ -81,6 +81,11 @@ func (c *clhClientMock) BootVM(ctx context.Context) (*http.Response, error) { return nil, nil } +//nolint:golint +func (c *clhClientMock) VmResizePut(ctx context.Context, vmResize chclient.VmResize) (*http.Response, error) { + return nil, nil +} + func TestCloudHypervisorAddVSock(t *testing.T) { assert := assert.New(t) clh := cloudHypervisor{}