Repository: kubermatic/machine-controller
Pull request #1483 (2022-11-15 15:37:54)

Anexia Provider: Utilize `Creating` state instead of blocking `Create` call
**What this PR does / why we need it**:

The `machine-controller` calls the providers `Create` method sequentially and only starts with the provisioning of another instance after `Create` returned without any errors. This PR changes the

- `Create` method to not block until the VM provisioning has completed 
- `Get` method to check via the instance status whether `InstanceID` and `ProvisioningID` are set

and utilizes the `Creating` state until the VM is ready.

This reduces the provisioning duration of node pools with more than one replica significantly as there are now only a couple of seconds (~10s) between the provisioning starts.


**Special notes for your reviewer**:

**Does this PR introduce a user-facing change? Then add your Release Note here**:
<!--
Write your release note:
1. Enter your extended release note in the below block. If the PR requires additional action from users switching to the new release, include the string "action required".
2. If no release note is required, just write "NONE".
-->
```release-note
Anexia: Refactor code to reduce provisioning time for node pools with multiple replicas.
```

**Documentation**:
<!--
Please do one of the following options:
- Add a link to the existing documentation
- Add a link to the kubermatic/docs pull request
- If no documentation change is applicable then add:
  - TBD (documentation will be added later)
  - NONE (no documentation needed for this PR)
-->
```documentation
NONE
```


kubermatic-bot: @marioreggiori: Adding the "do-not-merge/docs-needed" label because no documentation block was detected, please follow our [documentation process](https://github.com/kubermatic/docs/blob/master/PROCESS.md) to remove it.

<details>

Instructions for interacting with me using PR comments are available [here](https://git.k8s.io/community/contributors/guide/pull-requests.md).  If you have questions or suggestions related to my behavior, please file an issue against the [kubernetes/test-infra](https://github.com/kubernetes/test-infra/issues/new?title=Prow%20issue:) repository.
</details>
kubermatic-bot: @marioreggiori: Adding the "do-not-merge/release-note-label-needed" label because no release-note block was detected, please follow our [release note process](https://git.k8s.io/community/contributors/guide/release-notes.md) to remove it.

<details>

Instructions for interacting with me using PR comments are available [here](https://git.k8s.io/community/contributors/guide/pull-requests.md).  If you have questions or suggestions related to my behavior, please file an issue against the [kubernetes/test-infra](https://github.com/kubernetes/test-infra/issues/new?title=Prow%20issue:) repository.
</details>
kubermatic-bot: [APPROVALNOTIFIER] This PR is **NOT APPROVED**

This pull-request has been approved by: *<a href="https://github.com/kubermatic/machine-controller/pull/1483#" title="Author self-approved">marioreggiori</a>*
**Once this PR has been reviewed and has the lgtm label**, please assign ahmedwaleedmalik for approval by writing `/assign @ahmedwaleedmalik` in a comment. For more information see:[The Kubernetes Code Review Process](https://git.k8s.io/community/contributors/guide/owners.md#the-code-review-process).

The full list of commands accepted by this bot can be found [here](https://go.k8s.io/bot-commands?repo=kubermatic%2Fmachine-controller).

<details open>
Needs approval from an approver in each of these files:

- **[pkg/cloudprovider/provider/OWNERS](https://github.com/kubermatic/machine-controller/blob/main/pkg/cloudprovider/provider/OWNERS)**

Approvers can indicate their approval by writing `/approve` in a comment
Approvers can cancel approval by writing `/approve cancel` in a comment
</details>
<!-- META={"approvers":["ahmedwaleedmalik"]} -->
kubermatic-bot: Hi @marioreggiori. Thanks for your PR.

I'm waiting for a [kubermatic](https://github.com/orgs/kubermatic/people) member to verify that this patch is reasonable to test. If it is, they should reply with `/ok-to-test` on its own line. Until that is done, I will not automatically test new commits in this PR, but the usual testing commands by org members will still work. Regular contributors should [join the org](https://github.com/orgs/kubermatic/people) to skip this step.

Once the patch is verified, the new status will be reflected by the `ok-to-test` label.

I understand the commands that are listed [here](https://go.k8s.io/bot-commands?repo=kubermatic%2Fmachine-controller).

<details>

Instructions for interacting with me using PR comments are available [here](https://git.k8s.io/community/contributors/guide/pull-requests.md).  If you have questions or suggestions related to my behavior, please file an issue against the [kubernetes/test-infra](https://github.com/kubernetes/test-infra/issues/new?title=Prow%20issue:) repository.
</details>

ahmedwaleedmalik: /ok-to-test
kubermatic-bot: @marioreggiori: The following test **failed**, say `/retest` to rerun all failed tests or `/retest-required` to rerun all mandatory failed tests:

Test name | Commit | Details | Required | Rerun command
--- | --- | --- | --- | ---
pull-machine-controller-lint | 1cca75e3db0edafabbd24426f2e8c32ed6003c59 | [link](https://public-prow.loodse.com/view/gs/prow-dev-public-data/pr-logs/pull/kubermatic_machine-controller/1483/pull-machine-controller-lint/1592623014141235200) | true | `/test pull-machine-controller-lint`

[Full PR test history](https://public-prow.loodse.com/pr-history?org=kubermatic&repo=machine-controller&pr=1483)

<details>

Instructions for interacting with me using PR comments are available [here](https://git.k8s.io/community/contributors/guide/pull-requests.md).  If you have questions or suggestions related to my behavior, please file an issue against the [kubernetes/test-infra](https://github.com/kubernetes/test-infra/issues/new?title=Prow%20issue:) repository. I understand the commands that are listed [here](https://go.k8s.io/bot-commands).
</details>
<!-- test report -->
kubermatic-bot: @marioreggiori: The following tests **failed**, say `/retest` to rerun all failed tests or `/retest-required` to rerun all mandatory failed tests:

Test name | Commit | Details | Required | Rerun command
--- | --- | --- | --- | ---
pull-machine-controller-lint | 1cca75e3db0edafabbd24426f2e8c32ed6003c59 | [link](https://public-prow.loodse.com/view/gs/prow-dev-public-data/pr-logs/pull/kubermatic_machine-controller/1483/pull-machine-controller-lint/1592623014141235200) | true | `/test pull-machine-controller-lint`
pull-machine-controller-e2e-vsphere | 1cca75e3db0edafabbd24426f2e8c32ed6003c59 | [link](https://public-prow.loodse.com/view/gs/prow-dev-public-data/pr-logs/pull/kubermatic_machine-controller/1483/pull-machine-controller-e2e-vsphere/1592623011641430016) | true | `/test pull-machine-controller-e2e-vsphere`

[Full PR test history](https://public-prow.loodse.com/pr-history?org=kubermatic&repo=machine-controller&pr=1483)

<details>

Instructions for interacting with me using PR comments are available [here](https://git.k8s.io/community/contributors/guide/pull-requests.md).  If you have questions or suggestions related to my behavior, please file an issue against the [kubernetes/test-infra](https://github.com/kubernetes/test-infra/issues/new?title=Prow%20issue:) repository. I understand the commands that are listed [here](https://go.k8s.io/bot-commands).
</details>
<!-- test report -->
kubermatic-bot: @marioreggiori: The following tests **failed**, say `/retest` to rerun all failed tests or `/retest-required` to rerun all mandatory failed tests:

Test name | Commit | Details | Required | Rerun command
--- | --- | --- | --- | ---
pull-machine-controller-lint | 1cca75e3db0edafabbd24426f2e8c32ed6003c59 | [link](https://public-prow.loodse.com/view/gs/prow-dev-public-data/pr-logs/pull/kubermatic_machine-controller/1483/pull-machine-controller-lint/1592623014141235200) | true | `/test pull-machine-controller-lint`
pull-machine-controller-e2e-vsphere | 1cca75e3db0edafabbd24426f2e8c32ed6003c59 | [link](https://public-prow.loodse.com/view/gs/prow-dev-public-data/pr-logs/pull/kubermatic_machine-controller/1483/pull-machine-controller-e2e-vsphere/1592623011641430016) | true | `/test pull-machine-controller-e2e-vsphere`
pull-machine-controller-e2e-aws-legacy-userdata | 1cca75e3db0edafabbd24426f2e8c32ed6003c59 | [link](https://public-prow.loodse.com/view/gs/prow-dev-public-data/pr-logs/pull/kubermatic_machine-controller/1483/pull-machine-controller-e2e-aws-legacy-userdata/1592623004393672704) | true | `/test pull-machine-controller-e2e-aws-legacy-userdata`

[Full PR test history](https://public-prow.loodse.com/pr-history?org=kubermatic&repo=machine-controller&pr=1483)

<details>

Instructions for interacting with me using PR comments are available [here](https://git.k8s.io/community/contributors/guide/pull-requests.md).  If you have questions or suggestions related to my behavior, please file an issue against the [kubernetes/test-infra](https://github.com/kubernetes/test-infra/issues/new?title=Prow%20issue:) repository. I understand the commands that are listed [here](https://go.k8s.io/bot-commands).
</details>
<!-- test report -->
kubermatic-bot: @marioreggiori: The following tests **failed**, say `/retest` to rerun all failed tests or `/retest-required` to rerun all mandatory failed tests:

Test name | Commit | Details | Required | Rerun command
--- | --- | --- | --- | ---
pull-machine-controller-lint | 1cca75e3db0edafabbd24426f2e8c32ed6003c59 | [link](https://public-prow.loodse.com/view/gs/prow-dev-public-data/pr-logs/pull/kubermatic_machine-controller/1483/pull-machine-controller-lint/1592623014141235200) | true | `/test pull-machine-controller-lint`
pull-machine-controller-e2e-vsphere | 1cca75e3db0edafabbd24426f2e8c32ed6003c59 | [link](https://public-prow.loodse.com/view/gs/prow-dev-public-data/pr-logs/pull/kubermatic_machine-controller/1483/pull-machine-controller-e2e-vsphere/1592623011641430016) | true | `/test pull-machine-controller-e2e-vsphere`
pull-machine-controller-e2e-aws-legacy-userdata | 1cca75e3db0edafabbd24426f2e8c32ed6003c59 | [link](https://public-prow.loodse.com/view/gs/prow-dev-public-data/pr-logs/pull/kubermatic_machine-controller/1483/pull-machine-controller-e2e-aws-legacy-userdata/1592623004393672704) | true | `/test pull-machine-controller-e2e-aws-legacy-userdata`
pull-machine-controller-e2e-aws-spot-instance | 1cca75e3db0edafabbd24426f2e8c32ed6003c59 | [link](https://public-prow.loodse.com/view/gs/prow-dev-public-data/pr-logs/pull/kubermatic_machine-controller/1483/pull-machine-controller-e2e-aws-spot-instance/1592623004641136640) | true | `/test pull-machine-controller-e2e-aws-spot-instance`

[Full PR test history](https://public-prow.loodse.com/pr-history?org=kubermatic&repo=machine-controller&pr=1483)

<details>

Instructions for interacting with me using PR comments are available [here](https://git.k8s.io/community/contributors/guide/pull-requests.md).  If you have questions or suggestions related to my behavior, please file an issue against the [kubernetes/test-infra](https://github.com/kubernetes/test-infra/issues/new?title=Prow%20issue:) repository. I understand the commands that are listed [here](https://go.k8s.io/bot-commands).
</details>
<!-- test report -->
kubermatic-bot: @marioreggiori: The following tests **failed**, say `/retest` to rerun all failed tests or `/retest-required` to rerun all mandatory failed tests:

Test name | Commit | Details | Required | Rerun command
--- | --- | --- | --- | ---
pull-machine-controller-lint | 1cca75e3db0edafabbd24426f2e8c32ed6003c59 | [link](https://public-prow.loodse.com/view/gs/prow-dev-public-data/pr-logs/pull/kubermatic_machine-controller/1483/pull-machine-controller-lint/1592623014141235200) | true | `/test pull-machine-controller-lint`
pull-machine-controller-e2e-vsphere | 1cca75e3db0edafabbd24426f2e8c32ed6003c59 | [link](https://public-prow.loodse.com/view/gs/prow-dev-public-data/pr-logs/pull/kubermatic_machine-controller/1483/pull-machine-controller-e2e-vsphere/1592623011641430016) | true | `/test pull-machine-controller-e2e-vsphere`
pull-machine-controller-e2e-aws-legacy-userdata | 1cca75e3db0edafabbd24426f2e8c32ed6003c59 | [link](https://public-prow.loodse.com/view/gs/prow-dev-public-data/pr-logs/pull/kubermatic_machine-controller/1483/pull-machine-controller-e2e-aws-legacy-userdata/1592623004393672704) | true | `/test pull-machine-controller-e2e-aws-legacy-userdata`
pull-machine-controller-e2e-aws-spot-instance | 1cca75e3db0edafabbd24426f2e8c32ed6003c59 | [link](https://public-prow.loodse.com/view/gs/prow-dev-public-data/pr-logs/pull/kubermatic_machine-controller/1483/pull-machine-controller-e2e-aws-spot-instance/1592623004641136640) | true | `/test pull-machine-controller-e2e-aws-spot-instance`
pull-machine-controller-e2e-aws | 1cca75e3db0edafabbd24426f2e8c32ed6003c59 | [link](https://public-prow.loodse.com/view/gs/prow-dev-public-data/pr-logs/pull/kubermatic_machine-controller/1483/pull-machine-controller-e2e-aws/1592623004355923968) | true | `/test pull-machine-controller-e2e-aws`

[Full PR test history](https://public-prow.loodse.com/pr-history?org=kubermatic&repo=machine-controller&pr=1483)

<details>

Instructions for interacting with me using PR comments are available [here](https://git.k8s.io/community/contributors/guide/pull-requests.md).  If you have questions or suggestions related to my behavior, please file an issue against the [kubernetes/test-infra](https://github.com/kubernetes/test-infra/issues/new?title=Prow%20issue:) repository. I understand the commands that are listed [here](https://go.k8s.io/bot-commands).
</details>
<!-- test report -->
ahmedwaleedmalik: Hey @marioreggiori, can you please fix the liniting errors from `pull-machine-controller-lint`? 

```
level=info msg="[runner] linters took 36.340750268s with stages: goanalysis_metalinter: 36.290770629s, structcheck: 16.344µs, wastedassign: 12.051µs"
pkg/cloudprovider/provider/anexia/helper_test.go:67:6: `createSearchHandler` is unused (deadcode)
func createSearchHandler(t *testing.T, iterations int) http.HandlerFunc {
```

You can ignore the rest of the CI jobs. 
ahmedwaleedmalik: /test pull-machine-controller-e2e-anexia
kubermatic-bot: @marioreggiori: The following tests **failed**, say `/retest` to rerun all failed tests or `/retest-required` to rerun all mandatory failed tests:

Test name | Commit | Details | Required | Rerun command
--- | --- | --- | --- | ---
pull-machine-controller-e2e-aws-legacy-userdata | 1cca75e3db0edafabbd24426f2e8c32ed6003c59 | [link](https://public-prow.loodse.com/view/gs/prow-dev-public-data/pr-logs/pull/kubermatic_machine-controller/1483/pull-machine-controller-e2e-aws-legacy-userdata/1592623004393672704) | true | `/test pull-machine-controller-e2e-aws-legacy-userdata`
pull-machine-controller-e2e-aws-spot-instance | 1cca75e3db0edafabbd24426f2e8c32ed6003c59 | [link](https://public-prow.loodse.com/view/gs/prow-dev-public-data/pr-logs/pull/kubermatic_machine-controller/1483/pull-machine-controller-e2e-aws-spot-instance/1592623004641136640) | true | `/test pull-machine-controller-e2e-aws-spot-instance`
pull-machine-controller-e2e-vsphere | 2e2c7a0c1f263476da30a19d2db7abed194a0233 | [link](https://public-prow.loodse.com/view/gs/prow-dev-public-data/pr-logs/pull/kubermatic_machine-controller/1483/pull-machine-controller-e2e-vsphere/1592769800403161088) | true | `/test pull-machine-controller-e2e-vsphere`
pull-machine-controller-e2e-aws | 2e2c7a0c1f263476da30a19d2db7abed194a0233 | [link](https://public-prow.loodse.com/view/gs/prow-dev-public-data/pr-logs/pull/kubermatic_machine-controller/1483/pull-machine-controller-e2e-aws/1592769793113460736) | true | `/test pull-machine-controller-e2e-aws`

[Full PR test history](https://public-prow.loodse.com/pr-history?org=kubermatic&repo=machine-controller&pr=1483)

<details>

Instructions for interacting with me using PR comments are available [here](https://git.k8s.io/community/contributors/guide/pull-requests.md).  If you have questions or suggestions related to my behavior, please file an issue against the [kubernetes/test-infra](https://github.com/kubernetes/test-infra/issues/new?title=Prow%20issue:) repository. I understand the commands that are listed [here](https://go.k8s.io/bot-commands).
</details>
<!-- test report -->
kubermatic-bot: @marioreggiori: The following tests **failed**, say `/retest` to rerun all failed tests or `/retest-required` to rerun all mandatory failed tests:

Test name | Commit | Details | Required | Rerun command
--- | --- | --- | --- | ---
pull-machine-controller-e2e-aws-legacy-userdata | 1cca75e3db0edafabbd24426f2e8c32ed6003c59 | [link](https://public-prow.loodse.com/view/gs/prow-dev-public-data/pr-logs/pull/kubermatic_machine-controller/1483/pull-machine-controller-e2e-aws-legacy-userdata/1592623004393672704) | true | `/test pull-machine-controller-e2e-aws-legacy-userdata`
pull-machine-controller-e2e-vsphere | 2e2c7a0c1f263476da30a19d2db7abed194a0233 | [link](https://public-prow.loodse.com/view/gs/prow-dev-public-data/pr-logs/pull/kubermatic_machine-controller/1483/pull-machine-controller-e2e-vsphere/1592769800403161088) | true | `/test pull-machine-controller-e2e-vsphere`
pull-machine-controller-e2e-aws | 2e2c7a0c1f263476da30a19d2db7abed194a0233 | [link](https://public-prow.loodse.com/view/gs/prow-dev-public-data/pr-logs/pull/kubermatic_machine-controller/1483/pull-machine-controller-e2e-aws/1592769793113460736) | true | `/test pull-machine-controller-e2e-aws`
pull-machine-controller-e2e-aws-spot-instance | 2e2c7a0c1f263476da30a19d2db7abed194a0233 | [link](https://public-prow.loodse.com/view/gs/prow-dev-public-data/pr-logs/pull/kubermatic_machine-controller/1483/pull-machine-controller-e2e-aws-spot-instance/1592769793339953152) | true | `/test pull-machine-controller-e2e-aws-spot-instance`

[Full PR test history](https://public-prow.loodse.com/pr-history?org=kubermatic&repo=machine-controller&pr=1483)

<details>

Instructions for interacting with me using PR comments are available [here](https://git.k8s.io/community/contributors/guide/pull-requests.md).  If you have questions or suggestions related to my behavior, please file an issue against the [kubernetes/test-infra](https://github.com/kubernetes/test-infra/issues/new?title=Prow%20issue:) repository. I understand the commands that are listed [here](https://go.k8s.io/bot-commands).
</details>
<!-- test report -->
kubermatic-bot: @marioreggiori: The following tests **failed**, say `/retest` to rerun all failed tests or `/retest-required` to rerun all mandatory failed tests:

Test name | Commit | Details | Required | Rerun command
--- | --- | --- | --- | ---
pull-machine-controller-e2e-vsphere | 2e2c7a0c1f263476da30a19d2db7abed194a0233 | [link](https://public-prow.loodse.com/view/gs/prow-dev-public-data/pr-logs/pull/kubermatic_machine-controller/1483/pull-machine-controller-e2e-vsphere/1592769800403161088) | true | `/test pull-machine-controller-e2e-vsphere`
pull-machine-controller-e2e-aws | 2e2c7a0c1f263476da30a19d2db7abed194a0233 | [link](https://public-prow.loodse.com/view/gs/prow-dev-public-data/pr-logs/pull/kubermatic_machine-controller/1483/pull-machine-controller-e2e-aws/1592769793113460736) | true | `/test pull-machine-controller-e2e-aws`
pull-machine-controller-e2e-aws-spot-instance | 2e2c7a0c1f263476da30a19d2db7abed194a0233 | [link](https://public-prow.loodse.com/view/gs/prow-dev-public-data/pr-logs/pull/kubermatic_machine-controller/1483/pull-machine-controller-e2e-aws-spot-instance/1592769793339953152) | true | `/test pull-machine-controller-e2e-aws-spot-instance`
pull-machine-controller-e2e-aws-legacy-userdata | 2e2c7a0c1f263476da30a19d2db7abed194a0233 | [link](https://public-prow.loodse.com/view/gs/prow-dev-public-data/pr-logs/pull/kubermatic_machine-controller/1483/pull-machine-controller-e2e-aws-legacy-userdata/1592769793151209472) | true | `/test pull-machine-controller-e2e-aws-legacy-userdata`

[Full PR test history](https://public-prow.loodse.com/pr-history?org=kubermatic&repo=machine-controller&pr=1483)

<details>

Instructions for interacting with me using PR comments are available [here](https://git.k8s.io/community/contributors/guide/pull-requests.md).  If you have questions or suggestions related to my behavior, please file an issue against the [kubernetes/test-infra](https://github.com/kubernetes/test-infra/issues/new?title=Prow%20issue:) repository. I understand the commands that are listed [here](https://go.k8s.io/bot-commands).
</details>
<!-- test report -->

Patch:
diff --git a/pkg/cloudprovider/provider/anexia/instance.go b/pkg/cloudprovider/provider/anexia/instance.go
index fa53467a..cd67d80c 100644
--- a/pkg/cloudprovider/provider/anexia/instance.go
+++ b/pkg/cloudprovider/provider/anexia/instance.go
@@ -28,6 +28,7 @@ import (
 )
 
 type anexiaInstance struct {
+	isCreating        bool
 	info              *info.Info
 	reservedAddresses []string
 }
@@ -85,6 +86,10 @@ func (ai *anexiaInstance) Addresses() map[string]v1.NodeAddressType {
 }
 
 func (ai *anexiaInstance) Status() instance.Status {
+	if ai.isCreating {
+		return instance.StatusCreating
+	}
+
 	if ai.info != nil {
 		if ai.info.Status == anxtypes.MachinePoweredOn {
 			return instance.StatusRunning
diff --git a/pkg/cloudprovider/provider/anexia/provider.go b/pkg/cloudprovider/provider/anexia/provider.go
index 84172372..b8976330 100644
--- a/pkg/cloudprovider/provider/anexia/provider.go
+++ b/pkg/cloudprovider/provider/anexia/provider.go
@@ -23,6 +23,8 @@ import (
 	"errors"
 	"fmt"
 	"net/http"
+	"strings"
+	"sync"
 	"time"
 
 	anxclient "go.anx.io/go-anxcloud/pkg/client"
@@ -45,7 +47,6 @@ import (
 	v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 	"k8s.io/apimachinery/pkg/runtime"
 	k8stypes "k8s.io/apimachinery/pkg/types"
-	"k8s.io/apimachinery/pkg/util/wait"
 	"k8s.io/klog"
 )
 
@@ -115,16 +116,6 @@ func (p *provider) Create(ctx context.Context, machine *clusterv1alpha1.Machine,
 		retErr = anxtypes.NewMultiError(retErr, updateMachineStatus(machine, status, data.Update))
 	}()
 
-	// check whether machine is already provisioning
-	if isAlreadyProvisioning(ctx) && status.ProvisioningID == "" {
-		klog.Info("ongoing provisioning detected")
-		err := waitForVM(ctx, client)
-		if err != nil {
-			return nil, err
-		}
-		return p.Get(ctx, machine, data)
-	}
-
 	// provision machine
 	err = provisionVM(ctx, client)
 	if err != nil {
@@ -133,33 +124,6 @@ func (p *provider) Create(ctx context.Context, machine *clusterv1alpha1.Machine,
 	return p.Get(ctx, machine, data)
 }
 
-func waitForVM(ctx context.Context, client anxclient.Client) error {
-	reconcileContext := getReconcileContext(ctx)
-	api := vsphere.NewAPI(client)
-	var identifier string
-	err := wait.PollImmediate(5*time.Second, 1*time.Minute, func() (bool, error) {
-		klog.V(2).Info("checking for VM with name ", reconcileContext.Machine.Name)
-		vms, err := api.Search().ByName(ctx, fmt.Sprintf("%%-%s", reconcileContext.Machine.Name))
-		if err != nil {
-			return false, nil
-		}
-		if len(vms) < 1 {
-			return false, nil
-		}
-		if len(vms) > 1 {
-			return false, errors.New("too many VMs returned by search")
-		}
-		identifier = vms[0].Identifier
-		return true, nil
-	})
-	if err != nil {
-		return err
-	}
-
-	reconcileContext.Status.InstanceID = identifier
-	return updateMachineStatus(reconcileContext.Machine, *reconcileContext.Status, reconcileContext.ProviderData.Update)
-}
-
 func provisionVM(ctx context.Context, client anxclient.Client) error {
 	reconcileContext := getReconcileContext(ctx)
 	vmAPI := vsphere.NewAPI(client)
@@ -229,15 +193,6 @@ func provisionVM(ctx context.Context, client anxclient.Client) error {
 	klog.V(2).Info(fmt.Sprintf("Using provisionID from machine '%s' to await completion",
 		reconcileContext.Machine.Name))
 
-	instanceID, err := vmAPI.Provisioning().Progress().AwaitCompletion(ctx, status.ProvisioningID)
-	if err != nil {
-		klog.Errorf("failed to await machine completion '%s'", reconcileContext.Machine.Name)
-		// something went wrong remove provisioning ID, so we can start from scratch
-		status.ProvisioningID = ""
-		return newError(common.CreateMachineError, "instance provisioning failed: %v", err)
-	}
-
-	status.InstanceID = instanceID
 	meta.SetStatusCondition(&status.Conditions, v1.Condition{
 		Type:    ProvisionedType,
 		Status:  v1.ConditionTrue,
@@ -248,6 +203,8 @@ func provisionVM(ctx context.Context, client anxclient.Client) error {
 	return updateMachineStatus(reconcileContext.Machine, *status, reconcileContext.ProviderData.Update)
 }
 
+var _engsup3404mutex sync.Mutex
+
 func getIPAddress(ctx context.Context, client anxclient.Client) (string, error) {
 	reconcileContext := getReconcileContext(ctx)
 	status := reconcileContext.Status
@@ -258,6 +215,9 @@ func getIPAddress(ctx context.Context, client anxclient.Client) (string, error)
 		return status.ReservedIP, nil
 	}
 
+	_engsup3404mutex.Lock()
+	defer _engsup3404mutex.Unlock()
+
 	klog.Info(fmt.Sprintf("Creating a new IP for machine %q", reconcileContext.Machine.Name))
 	addrAPI := anxaddr.NewAPI(client)
 	config := reconcileContext.Config
@@ -447,7 +407,7 @@ func (p *provider) Validate(_ context.Context, machinespec clusterv1alpha1.Machi
 	return nil
 }
 
-func (p *provider) Get(ctx context.Context, machine *clusterv1alpha1.Machine, _ *cloudprovidertypes.ProviderData) (instance.Instance, error) {
+func (p *provider) Get(ctx context.Context, machine *clusterv1alpha1.Machine, pd *cloudprovidertypes.ProviderData) (instance.Instance, error) {
 	config, _, err := p.getConfig(machine.Spec.ProviderSpec)
 	if err != nil {
 		return nil, newError(common.InvalidConfigurationMachineError, "failed to retrieve config: %v", err)
@@ -464,10 +424,29 @@ func (p *provider) Get(ctx context.Context, machine *clusterv1alpha1.Machine, _
 		return nil, newError(common.InvalidConfigurationMachineError, "failed to get machine status: %v", err)
 	}
 
-	if status.InstanceID == "" {
+	if status.InstanceID == "" && status.ProvisioningID == "" {
 		return nil, cloudprovidererrors.ErrInstanceNotFound
 	}
 
+	if status.InstanceID == "" {
+		progress, err := vsphereAPI.Provisioning().Progress().Get(ctx, status.ProvisioningID)
+		if err != nil {
+			return nil, fmt.Errorf("failed to get provisioning progress: %w", err)
+		}
+		if len(progress.Errors) > 0 {
+			return nil, fmt.Errorf("vm provisioning had errors: %s", strings.Join(progress.Errors, ","))
+		}
+		if progress.Progress < 100 || progress.VMIdentifier == "" {
+			return &anexiaInstance{isCreating: true}, nil
+		}
+
+		status.InstanceID = progress.VMIdentifier
+
+		if err := updateMachineStatus(machine, status, pd.Update); err != nil {
+			return nil, fmt.Errorf("failed updating machine status: %w", err)
+		}
+	}
+
 	instance := anexiaInstance{}
 
 	if status.IPState == anxtypes.IPStateBound && status.ReservedIP != "" {
diff --git a/pkg/cloudprovider/provider/aws/provider.go b/pkg/cloudprovider/provider/aws/provider.go
index e72cdcae..a199954a 100644
--- a/pkg/cloudprovider/provider/aws/provider.go
+++ b/pkg/cloudprovider/provider/aws/provider.go
@@ -104,12 +104,12 @@ var (
 		// Source: https://wiki.centos.org/Cloud/AWS
 		providerconfigtypes.OperatingSystemCentOS: {
 			awstypes.CPUArchitectureX86_64: {
-				description: "CentOS 7* x86_64",
+				description: "CentOS Linux 7* x86_64*",
 				// The AWS marketplace ID from CentOS Community Platform Engineering (CPE)
 				owner: "125523088429",
 			},
 			awstypes.CPUArchitectureARM64: {
-				description: "CentOS 7* aarch64",
+				description: "CentOS Linux 7* aarch64*",
 				// The AWS marketplace ID from CentOS Community Platform Engineering (CPE)
 				owner: "125523088429",
 			},
diff --git a/pkg/cloudprovider/provider/kubevirt/provider.go b/pkg/cloudprovider/provider/kubevirt/provider.go
index b84a0154..9f535850 100644
--- a/pkg/cloudprovider/provider/kubevirt/provider.go
+++ b/pkg/cloudprovider/provider/kubevirt/provider.go
@@ -60,12 +60,18 @@ func init() {
 	}
 }
 
+type imageSource string
+
 const (
 	// topologyKeyHostname defines the topology key for the node hostname.
 	topologyKeyHostname = "kubernetes.io/hostname"
 	// machineDeploymentLabelKey defines the label key used to contains as value the MachineDeployment name
 	// which machine comes from.
 	machineDeploymentLabelKey = "md"
+	// httpSource defines the http source type for VM Disk Image.
+	httpSource imageSource = "http"
+	// pvcSource defines the pvc source type for VM Disk Image.
+	pvcSource imageSource = "pvc"
 )
 
 var supportedOS = map[providerconfigtypes.OperatingSystem]*struct{}{
@@ -93,7 +99,7 @@ type Config struct {
 	CPUs                      string
 	Memory                    string
 	Namespace                 string
-	OsImage                   OSImage
+	OSImageSource             *cdiv1beta1.DataVolumeSource
 	StorageClassName          string
 	PVCSize                   resource.Quantity
 	FlavorName                string
@@ -148,11 +154,6 @@ type SecondaryDisks struct {
 	StorageClassName string
 }
 
-type OSImage struct {
-	URL            string
-	DataVolumeName string
-}
-
 type kubeVirtServer struct {
 	vmi kubevirtv1.VirtualMachineInstance
 }
@@ -247,15 +248,12 @@ func (p *provider) getConfig(provSpec clusterv1alpha1.ProviderSpec) (*Config, *p
 		return nil, nil, fmt.Errorf(`failed to get value of "memory" field: %w`, err)
 	}
 	config.Namespace = getNamespace()
-	osImage, err := p.configVarResolver.GetConfigVarStringValue(rawConfig.VirtualMachine.Template.PrimaryDisk.OsImage)
+
+	config.OSImageSource, err = p.parseOSImageSource(rawConfig.VirtualMachine.Template.PrimaryDisk, config.Namespace)
 	if err != nil {
-		return nil, nil, fmt.Errorf(`failed to get value of "sourceURL" field: %w`, err)
-	}
-	if _, err = url.ParseRequestURI(osImage); err == nil {
-		config.OsImage.URL = osImage
-	} else {
-		config.OsImage.DataVolumeName = osImage
+		return nil, nil, fmt.Errorf(`failed to get value of "osImageSource" field: %w`, err)
 	}
+
 	pvcSize, err := p.configVarResolver.GetConfigVarStringValue(rawConfig.VirtualMachine.Template.PrimaryDisk.Size)
 	if err != nil {
 		return nil, nil, fmt.Errorf(`failed to get value of "pvcSize" field: %w`, err)
@@ -276,9 +274,6 @@ func (p *provider) getConfig(provSpec clusterv1alpha1.ProviderSpec) (*Config, *p
 	// Instancetype and Preference
 	config.Instancetype = rawConfig.VirtualMachine.Instancetype
 	config.Preference = rawConfig.VirtualMachine.Preference
-	if err != nil {
-		return nil, nil, fmt.Errorf(`failed to parse "preference" field: %w`, err)
-	}
 
 	dnsPolicyString, err := p.configVarResolver.GetConfigVarStringValue(rawConfig.VirtualMachine.DNSPolicy)
 	if err != nil {
@@ -377,6 +372,35 @@ func (p *provider) parseTopologySpreadConstraint(topologyConstraints []kubevirtt
 	return parsedTopologyConstraints, nil
 }
 
+func (p *provider) parseOSImageSource(primaryDisk kubevirttypes.PrimaryDisk, nameSpace string) (*cdiv1beta1.DataVolumeSource, error) {
+	osImage, err := p.configVarResolver.GetConfigVarStringValue(primaryDisk.OsImage)
+	if err != nil {
+		return nil, fmt.Errorf(`failed to get value of "primaryDisk.osImage" field: %w`, err)
+	}
+	osImageSource, err := p.configVarResolver.GetConfigVarStringValue(primaryDisk.Source)
+	if err != nil {
+		return nil, fmt.Errorf(`failed to get value of "primaryDisk.source" field: %w`, err)
+	}
+	switch imageSource(osImageSource) {
+	case httpSource:
+		return &cdiv1beta1.DataVolumeSource{HTTP: &cdiv1beta1.DataVolumeSourceHTTP{URL: osImage}}, nil
+	case pvcSource:
+		if namespaceAndName := strings.Split(osImage, "/"); len(namespaceAndName) >= 2 {
+			return &cdiv1beta1.DataVolumeSource{PVC: &cdiv1beta1.DataVolumeSourcePVC{Name: namespaceAndName[1], Namespace: namespaceAndName[0]}}, nil
+		}
+		return &cdiv1beta1.DataVolumeSource{PVC: &cdiv1beta1.DataVolumeSourcePVC{Name: osImage, Namespace: nameSpace}}, nil
+	default:
+		// handle old API for backward compatibility.
+		if _, err = url.ParseRequestURI(osImage); err == nil {
+			return &cdiv1beta1.DataVolumeSource{HTTP: &cdiv1beta1.DataVolumeSourceHTTP{URL: osImage}}, nil
+		}
+		if namespaceAndName := strings.Split(osImage, "/"); len(namespaceAndName) >= 2 {
+			return &cdiv1beta1.DataVolumeSource{PVC: &cdiv1beta1.DataVolumeSourcePVC{Name: namespaceAndName[1], Namespace: namespaceAndName[0]}}, nil
+		}
+		return &cdiv1beta1.DataVolumeSource{PVC: &cdiv1beta1.DataVolumeSourcePVC{Name: osImage, Namespace: nameSpace}}, nil
+	}
+}
+
 // getNamespace returns the namespace where the VM is created.
 // VM is created in a dedicated namespace <cluster-id>
 // which is the namespace where the machine-controller pod is running.
@@ -509,7 +533,11 @@ func (p *provider) MachineMetricsLabels(machine *clusterv1alpha1.Machine) (map[s
 	if err == nil {
 		labels["cpus"] = c.CPUs
 		labels["memoryMIB"] = c.Memory
-		labels["osImage"] = c.OsImage.URL
+		if c.OSImageSource.HTTP != nil {
+			labels["osImage"] = c.OSImageSource.HTTP.URL
+		} else if c.OSImageSource.PVC != nil {
+			labels["osImage"] = c.OSImageSource.PVC.Name
+		}
 	}
 
 	return labels, err
@@ -790,7 +818,6 @@ func getVMVolumes(config *Config, dataVolumeName string, userDataSecretName stri
 }
 
 func getDataVolumeTemplates(config *Config, dataVolumeName string) []kubevirtv1.DataVolumeTemplateSpec {
-	dataVolumeSource := getDataVolumeSource(config.OsImage)
 	pvcRequest := corev1.ResourceList{corev1.ResourceStorage: config.PVCSize}
 	dataVolumeTemplates := []kubevirtv1.DataVolumeTemplateSpec{
 		{
@@ -807,7 +834,7 @@ func getDataVolumeTemplates(config *Config, dataVolumeName string) []kubevirtv1.
 						Requests: pvcRequest,
 					},
 				},
-				Source: dataVolumeSource,
+				Source: config.OSImageSource,
 			},
 		},
 	}
@@ -826,29 +853,13 @@ func getDataVolumeTemplates(config *Config, dataVolumeName string) []kubevirtv1.
 						Requests: corev1.ResourceList{corev1.ResourceStorage: sd.Size},
 					},
 				},
-				Source: dataVolumeSource,
+				Source: config.OSImageSource,
 			},
 		})
 	}
 	return dataVolumeTemplates
 }
 
-// getDataVolumeSource returns DataVolumeSource, HTTP or PVC.
-func getDataVolumeSource(osImage OSImage) *cdiv1beta1.DataVolumeSource {
-	dataVolumeSource := &cdiv1beta1.DataVolumeSource{}
-	if osImage.URL != "" {
-		dataVolumeSource.HTTP = &cdiv1beta1.DataVolumeSourceHTTP{URL: osImage.URL}
-	} else if osImage.DataVolumeName != "" {
-		if nameSpaceAndName := strings.Split(osImage.DataVolumeName, "/"); len(nameSpaceAndName) >= 2 {
-			dataVolumeSource.PVC = &cdiv1beta1.DataVolumeSourcePVC{
-				Namespace: nameSpaceAndName[0],
-				Name:      nameSpaceAndName[1],
-			}
-		}
-	}
-	return dataVolumeSource
-}
-
 func getAffinity(config *Config, matchKey, matchValue string) *corev1.Affinity {
 	affinity := &corev1.Affinity{}
 
diff --git a/pkg/cloudprovider/provider/kubevirt/types/types.go b/pkg/cloudprovider/provider/kubevirt/types/types.go
index 365171d0..a0c6d35a 100644
--- a/pkg/cloudprovider/provider/kubevirt/types/types.go
+++ b/pkg/cloudprovider/provider/kubevirt/types/types.go
@@ -67,6 +67,8 @@ type Template struct {
 type PrimaryDisk struct {
 	Disk
 	OsImage providerconfigtypes.ConfigVarString `json:"osImage,omitempty"`
+	// Source describes the VM Disk Image source.
+	Source providerconfigtypes.ConfigVarString `json:"source,omitempty"`
 }
 
 // SecondaryDisks.


Test patch:
diff --git a/pkg/cloudprovider/provider/anexia/helper_test.go b/pkg/cloudprovider/provider/anexia/helper_test.go
index 0bcea21f..38c3a37e 100644
--- a/pkg/cloudprovider/provider/anexia/helper_test.go
+++ b/pkg/cloudprovider/provider/anexia/helper_test.go
@@ -18,11 +18,9 @@ package anexia
 
 import (
 	"encoding/json"
-	"net/http"
 	"testing"
 
 	"github.com/gophercloud/gophercloud/testhelper"
-	"go.anx.io/go-anxcloud/pkg/vsphere/search"
 
 	"github.com/kubermatic/machine-controller/pkg/apis/cluster/v1alpha1"
 	anxtypes "github.com/kubermatic/machine-controller/pkg/cloudprovider/provider/anexia/types"
@@ -64,27 +62,6 @@ func getSpecsForValidationTest(t *testing.T, configCases []ConfigTestCase) []Val
 	return testCases
 }
 
-func createSearchHandler(t *testing.T, iterations int) http.HandlerFunc {
-	counter := 0
-	return func(writer http.ResponseWriter, request *http.Request) {
-		test := request.URL.Query().Get("name")
-		testhelper.AssertEquals(t, "%-TestMachine", test)
-		testhelper.TestMethod(t, request, http.MethodGet)
-		if iterations == counter {
-			encoder := json.NewEncoder(writer)
-			testhelper.AssertNoErr(t, encoder.Encode(map[string]interface{}{
-				"data": []search.VM{
-					{
-						Name:       "543053-TestMachine",
-						Identifier: TestIdentifier,
-					},
-				},
-			}))
-		}
-		counter++
-	}
-}
-
 func newConfigVarString(str string) types.ConfigVarString {
 	return types.ConfigVarString{
 		Value: str,
diff --git a/pkg/cloudprovider/provider/anexia/provider_test.go b/pkg/cloudprovider/provider/anexia/provider_test.go
index b26d29f3..08cec325 100644
--- a/pkg/cloudprovider/provider/anexia/provider_test.go
+++ b/pkg/cloudprovider/provider/anexia/provider_test.go
@@ -50,38 +50,6 @@ func TestAnexiaProvider(t *testing.T) {
 		server.Close()
 	})
 
-	t.Run("Test waiting for VM", func(t *testing.T) {
-		t.Parallel()
-
-		waitUntilVMIsFound := 2
-		testhelper.Mux.HandleFunc("/api/vsphere/v1/search/by_name.json", createSearchHandler(t, waitUntilVMIsFound))
-
-		providerStatus := anxtypes.ProviderStatus{}
-		ctx := createReconcileContext(context.Background(), reconcileContext{
-			Machine: &v1alpha1.Machine{
-				ObjectMeta: metav1.ObjectMeta{Name: "TestMachine"},
-			},
-			Status:   &providerStatus,
-			UserData: "",
-			Config:   resolvedConfig{},
-
-			ProviderData: &cloudprovidertypes.ProviderData{
-				Update: func(m *clusterv1alpha1.Machine, mod ...cloudprovidertypes.MachineModifier) error {
-					return nil
-				},
-			},
-		})
-
-		err := waitForVM(ctx, client)
-		if err != nil {
-			t.Fatal("No error was expected", err)
-		}
-
-		if providerStatus.InstanceID != TestIdentifier {
-			t.Error("Expected InstanceID to be set")
-		}
-	})
-
 	t.Run("Test provision VM", func(t *testing.T) {
 		t.Parallel()
 		testhelper.Mux.HandleFunc("/api/ipam/v1/address/reserve/ip/count.json", func(writer http.ResponseWriter, request *http.Request) {
diff --git a/pkg/cloudprovider/provider/kubevirt/provider_test.go b/pkg/cloudprovider/provider/kubevirt/provider_test.go
index 3af0d314..38e8933c 100644
--- a/pkg/cloudprovider/provider/kubevirt/provider_test.go
+++ b/pkg/cloudprovider/provider/kubevirt/provider_test.go
@@ -103,6 +103,7 @@ type kubevirtProviderSpecConf struct {
 	TopologySpreadConstraint bool
 	Affinity                 bool
 	SecondaryDisks           bool
+	OsImageSource            imageSource
 }
 
 func (k kubevirtProviderSpecConf) rawProviderSpec(t *testing.T) []byte {
@@ -167,7 +168,12 @@ func (k kubevirtProviderSpecConf) rawProviderSpec(t *testing.T) []byte {
 					"osImage": "http://x.y.z.t/ubuntu.img",
 					{{- end }}
 					"size": "10Gi",
+					{{- if .OsImageSource }}
+					"storageClassName": "longhorn",
+					"source": "{{ .OsImageSource }}"
+					{{- else }}
 					"storageClassName": "longhorn"
+					{{- end }}
 				}
 			}
 		}
@@ -267,6 +273,14 @@ func TestNewVirtualMachine(t *testing.T) {
 			name:     "custom-local-disk",
 			specConf: kubevirtProviderSpecConf{OsImageDV: "ns/dvname"},
 		},
+		{
+			name:     "http-image-source",
+			specConf: kubevirtProviderSpecConf{OsImageSource: httpSource},
+		},
+		{
+			name:     "pvc-image-source",
+			specConf: kubevirtProviderSpecConf{OsImageSource: pvcSource, OsImageDV: "ns/dvname"},
+		},
 	}
 	for _, tt := range tests {
 		t.Run(tt.name, func(t *testing.T) {
diff --git a/pkg/cloudprovider/provider/kubevirt/testdata/http-image-source.yaml b/pkg/cloudprovider/provider/kubevirt/testdata/http-image-source.yaml
new file mode 100644
index 00000000..b488ce0c
--- /dev/null
+++ b/pkg/cloudprovider/provider/kubevirt/testdata/http-image-source.yaml
@@ -0,0 +1,72 @@
+apiVersion: kubevirt.io/v1
+kind: VirtualMachine
+metadata:
+  annotations:
+  labels:
+    kubevirt.io/vm: http-image-source
+    md: md-name
+  name: http-image-source
+  namespace: test-namespace
+spec:
+  dataVolumeTemplates:
+    - metadata:
+        name: http-image-source
+      spec:
+        pvc:
+          accessModes:
+            - ReadWriteOnce
+          resources:
+            requests:
+              storage: 10Gi
+          storageClassName: longhorn
+        source:
+          http:
+            url: http://x.y.z.t/ubuntu.img
+  running: true
+  template:
+    metadata:
+      creationTimestamp: null
+      labels:
+        kubevirt.io/vm: http-image-source
+        md: md-name
+    spec:
+      affinity: {}
+      domain:
+        devices:
+          disks:
+            - disk:
+                bus: virtio
+              name: datavolumedisk
+            - disk:
+                bus: virtio
+              name: cloudinitdisk
+          interfaces:
+            - macAddress: b6:f5:b4:fe:45:1d
+              name: default
+              bridge: {}
+        resources:
+          limits:
+            cpu: "2"
+            memory: 2Gi
+          requests:
+            cpu: "2"
+            memory: 2Gi
+      networks:
+        - name: default
+          pod: {}
+      terminationGracePeriodSeconds: 30
+      topologyspreadconstraints:
+        - maxskew: 1
+          topologykey: kubernetes.io/hostname
+          whenunsatisfiable: ScheduleAnyway
+          labelselector:
+            matchlabels:
+              md: md-name
+      volumes:
+        - dataVolume:
+            name: http-image-source
+          name: datavolumedisk
+        - cloudInitNoCloud:
+            secretRef:
+              name: udsn
+          name: cloudinitdisk
diff --git a/pkg/cloudprovider/provider/kubevirt/testdata/pvc-image-source.yaml b/pkg/cloudprovider/provider/kubevirt/testdata/pvc-image-source.yaml
new file mode 100644
index 00000000..809c87ab
--- /dev/null
+++ b/pkg/cloudprovider/provider/kubevirt/testdata/pvc-image-source.yaml
@@ -0,0 +1,73 @@
+apiVersion: kubevirt.io/v1
+kind: VirtualMachine
+metadata:
+  annotations:
+  labels:
+    kubevirt.io/vm: pvc-image-source
+    md: md-name
+  name: pvc-image-source
+  namespace: test-namespace
+spec:
+  dataVolumeTemplates:
+    - metadata:
+        name: pvc-image-source
+      spec:
+        pvc:
+          accessModes:
+            - ReadWriteOnce
+          resources:
+            requests:
+              storage: 10Gi
+          storageClassName: longhorn
+        source:
+          pvc:
+            namespace: ns
+            name: dvname
+  running: true
+  template:
+    metadata:
+      creationTimestamp: null
+      labels:
+        kubevirt.io/vm: pvc-image-source
+        md: md-name
+    spec:
+      affinity: {}
+      domain:
+        devices:
+          disks:
+            - disk:
+                bus: virtio
+              name: datavolumedisk
+            - disk:
+                bus: virtio
+              name: cloudinitdisk
+          interfaces:
+            - macAddress: b6:f5:b4:fe:45:1d
+              name: default
+              bridge: {}
+        resources:
+          limits:
+            cpu: "2"
+            memory: 2Gi
+          requests:
+            cpu: "2"
+            memory: 2Gi
+      networks:
+        - name: default
+          pod: {}
+      terminationGracePeriodSeconds: 30
+      topologyspreadconstraints:
+        - maxskew: 1
+          topologykey: kubernetes.io/hostname
+          whenunsatisfiable: ScheduleAnyway
+          labelselector:
+            matchlabels:
+              md: md-name
+      volumes:
+        - dataVolume:
+            name: pvc-image-source
+          name: datavolumedisk
+        - cloudInitNoCloud:
+            secretRef:
+              name: udsn
+          name: cloudinitdisk
diff --git a/test/e2e/provisioning/testdata/machinedeployment-vsphere-static-ip.yaml b/test/e2e/provisioning/testdata/machinedeployment-vsphere-static-ip.yaml
index a5407d5b..56ef49c9 100644
--- a/test/e2e/provisioning/testdata/machinedeployment-vsphere-static-ip.yaml
+++ b/test/e2e/provisioning/testdata/machinedeployment-vsphere-static-ip.yaml
@@ -33,7 +33,7 @@ spec:
             folder: '/Hamburg/vm/Kubermatic-ci'
             password: << VSPHERE_PASSWORD >>
             # example: 'https://your-vcenter:8443'. '/sdk' gets appended automatically
-            datastore: alpha1
+            datastore: ceph-vm
             cpus: 2
             MemoryMB: 2048
             allowInsecure: true
diff --git a/test/e2e/provisioning/testdata/machinedeployment-vsphere.yaml b/test/e2e/provisioning/testdata/machinedeployment-vsphere.yaml
index 5f1f969d..a7906ed6 100644
--- a/test/e2e/provisioning/testdata/machinedeployment-vsphere.yaml
+++ b/test/e2e/provisioning/testdata/machinedeployment-vsphere.yaml
@@ -33,7 +33,7 @@ spec:
             folder: '/Hamburg/vm/Kubermatic-ci'
             password: << VSPHERE_PASSWORD >>
             # example: 'https://your-vcenter:8443'. '/sdk' gets appended automatically
-            datastore: alpha1
+            datastore: ceph-vm
             cpus: 2
             MemoryMB: 4096
             diskSizeGB: << DISK_SIZE >>