vultr / ansible-collection-vultr

GNU General Public License v3.0
28 stars 17 forks source link

[BUG] - changing OS on VM doesn't seem to work or is not possible #126

Closed mattbrock closed 3 months ago

mattbrock commented 3 months ago

Changing the OS on a VM doesn't seem to work, or is not possible

I'm attempting to change the OS on a Vultr VM and either I haven't hit upon the required combination of parameters, or there's a bug in the operation, or it isn't currently possible.

To Reproduce

Ansible playbook is as follows. This is being run against a VM currently on AlmaLinux 8 in an attempt to change its OS to AlmaLinux 9:

---
- name: Vultr tasks for AlmaLinux 8 - 9 upgrade
  gather_facts: no  # yamllint disable-line rule:truthy
  hosts: localhost

  tasks:

    - name: Change VM OS in Vultr
      vultr.cloud.instance:
        label: "{{ vultr_vm_label }}"
        region: "lhr"
        plan: "vc2-2c-4gb"
        os: "AlmaLinux 9 x64"

I included region and plan, even though I'm not changing those and would prefer not to have to use them, because the module returned errors otherwise complaining those weren't defined.

The vultr_vm_label is being passed in as a variable, and the api_key is defined in the module config (in turn also passed in as variable).

When run, nothing is changed. Output is as follows:

{
  "changed": false,
  "vultr_instance": {
    "id": "xxxxxxxxxxxxxxxxxx",
    "os": "Alma Linux 8 x64",
    "ram": 4096,
    "disk": 80,
    "main_ip": "xxx.xxx.xxx.xxx",
    "vcpu_count": 2,
    "region": "lhr",
    "plan": "vc2-2c-4gb",
    "date_created": "2024-01-03T09:46:57+00:00",
    "status": "active",
    "allowed_bandwidth": 1915,
    "netmask_v4": "xxx.xxx.xxx.xxx",
    "gateway_v4": "xxx.xxx.xxx.xxx",
    "power_status": "running",
    "server_status": "ok",
    "v6_network": "",
    "v6_main_ip": "",
    "v6_network_size": 0,
    "label": "xxxxxxxxxxxxxxxx",
    "internal_ip": "xxx.xxx.xxx.xxx",
    "kvm": "https://my.vultr.com/subs/vps/novnc/api.php?data=xxxxxxxxxxxx",
    "hostname": "xxxxxxxxxxx",
    "tag": "environment: testing",
    "tags": [
      "environment: testing"
    ],
    "os_id": 452,
    "app_id": 0,
    "image_id": "",
    "firewall_group_id": "",
    "features": [
      "auto_backups"
    ],
    "user_scheme": "root",
    "pending_charges": 1.11,
    "backups": "enabled",
    "ddos_protection": false,
    "enable_ipv6": false,
    "vpcs": [
      {
        "id": "xxxxxxxxxx",
        "mac_address": "xxxxxxxxxx",
        "ip_address": "xxx.xxx.xxx.xxx",
        "description": "xxxxxxxxxxxx"
      }
    ],
    "user_data": ""
  },
  "diff": {
    "before": {},
    "after": {}
  },
  "vultr_api": {
    "api_timeout": 180,
    "api_retries": 5,
    "api_retry_max_delay": 12,
    "api_endpoint": "https://api.vultr.com/v2"
  },
  "invocation": {
    "module_args": {
      "label": "xxxxxxxxxxxxxxx",
      "region": "lhr",
      "plan": "vc2-2c-4gb",
      "os": "AlmaLinux 9 x64",
      "api_key": "VALUE_SPECIFIED_IN_NO_LOG_PARAMETER",
      "api_endpoint": "https://api.vultr.com/v2",
      "api_timeout": 180,
      "api_retries": 5,
      "api_retry_max_delay": 12,
      "validate_certs": true,
      "activation_email": false,
      "state": "present",
      "hostname": null,
      "app": null,
      "image": null,
      "snapshot": null,
      "ddos_protection": null,
      "backups": null,
      "enable_ipv6": null,
      "tags": null,
      "vpcs": null,
      "reserved_ipv4": null,
      "firewall_group": null,
      "startup_script": null,
      "user_data": null,
      "ssh_keys": null,
      "user_scheme": null,
      "os_id": 1868
    }
  },
  "_ansible_no_log": null
}

Expected behavior

I would have expected this to change the OS.

resmo commented 3 months ago

Hi @mattbrock, thanks for the report but it is not possible to change the OS by the API by design.

To update, simply upgrade the OS an existing VM itself to the new release or reinstall the VM instance using the desired OS parameter.

mattbrock commented 3 months ago

Hi @resmo.

I've determined that it is possible to change the OS via the Vultr API. I have just run a successful test as follows:

curl "https://api.vultr.com/v2/instances/xxxxxxxxxxxxxxxxxxxx" -X PATCH -H "Authorization: Bearer xxxxxxxxxxxxxxxxxxxx" -H "Content-Type: application/json" --data '{ "os_id" : "1868" }'

Which returns the following result:

{
  "instance": {
    "id": "xxxxxxxxxxxxxxxxxxxx",
    "os": "Alma Linux 9 x64",
    "ram": 4096,
    "disk": 0,
    "main_ip": "xxx.xxx.xxx.xxx",
    "vcpu_count": 2,
    "region": "lhr",
    "plan": "vc2-2c-4gb",
    "date_created": "2024-01-03T09:46:57+00:00",
    "status": "pending",
    "allowed_bandwidth": 2454,
    "netmask_v4": "xxx.xxx.xxx.xxx",
    "gateway_v4": "xxx.xxx.xxx.xxx",
    "power_status": "stopped",
    "server_status": "none",
    "v6_network": "",
    "v6_main_ip": "",
    "v6_network_size": 0,
    "label": "xxxxxxxxxxxxxxxxxxxx",
    "internal_ip": "xxx.xxx.xxx.xxx",
    "kvm": "",
    "hostname": "xxxxxxxxxxxxxxxxxxxx",
    "tag": "environment: testing",
    "tags": [
      "environment: testing"
    ],
    "os_id": 1868,
    "app_id": 0,
    "image_id": "",
    "firewall_group_id": "",
    "features": [
      "auto_backups"
    ],
    "user_scheme": "root",
    "pending_charges": 19.19,
    "default_password": "xxxxxxxxxxxxxxxxxxxx"
  }
}

I've confirmed that the OS has actually changed in the Vultr console.

So do you have any idea what the equivalent would be when using the Ansible module? Or if that ability doesn't currently exist in the Ansible module, can it be added?

resmo commented 3 months ago

Okay, I need to clarify my statement:

It is not possible to change the operating system without reinstalling the instance.

We could implement it in this collection, but I think that reinstalling by changing the OS param is not very common, but could potentially cause a lot of damage if you didn't mean to reinstall the OS, but accidentally changed the OS param.

The same behaviour could be achieved with a 2 task playbook:

This way, you know exactly what you are doing when you explicitly delete the VM. Further It could be even less "dangerous" by adding some DevOps mindset:

mattbrock commented 3 months ago

Thanks for the explanation. It makes sense. There are reasons in this case why reinstalling the instance is undesirable. This is specifically part of a complex automation procedure purely to upgrade the OS in a very precise way for reasons determined by the apps involved and complex network setup etc. Helpful to know this definitely isn't possible via the Ansible module. I shall investigate other possibilities. Cheers.

resmo commented 3 months ago

Hi @mattbrock again, to make it clear, it is possible with ansible modules! but not in a single task.

More like so:

- name: Delete an old instance
  vultr.cloud.instance:
    label: my web server
    region: ams
    state: absent

- name: Create the new instance
  vultr.cloud.instance:
    label: my web server
    hostname: my-hostname
    plan: vc2-1c-2gb
    ssh_keys:
      - my ssh key
    region: ams
    os: Debian 12 x64 (bookworm)
mattbrock commented 3 months ago

Yes, understood, but in this case it would be much better in many ways to avoid that as it involves a lot of needless complexity on the networking side etc. which is best avoided, so in this case I really do need to be able to just trigger a pure OS change and nothing more. Appreciate your engagement, thank you.

resmo commented 3 months ago

Yes, understood, but in this case it would be much better in many ways to avoid that as it involves a lot of needless complexity on the networking side etc. which is best avoided, so in this case I really do need to be able to just trigger a pure OS change and nothing more. Appreciate your engagement, thank you.

last but not least, if you really want to have it, it would be not much effort to fork (copy the module) into your project, make the changes (shouldn't be not more then few lines changed) and run it. If you like it, I could assist.

I would assume, it should work the way you want it just by adding os_id to this list: https://github.com/vultr/ansible-collection-vultr/blob/main/plugins/modules/instance.py#L594

jasites commented 3 months ago

Shouldn't you be able to update the state of the instance to reinstalled for one run and then change it back to 'present' (or omitted)?

resmo commented 3 months ago

I assume, reinstalled would reinstall the current OS but would not change the os_id

resmo commented 3 months ago

but, we could think of taking the os_id into consideration for an update in case of state=reinstalled.

mattbrock commented 3 months ago

Would be great to get involved in the code (via fork or whatever) and adapt as needed, but unfortunately I wouldn't have the availability for that currently.

Making the OS ID change via state=reinstalled sounds to me like a good solution, and quite logical.