{"id":25099625,"url":"https://github.com/mikejoh/devstack-on-kvm","last_synced_at":"2026-01-15T23:11:12.013Z","repository":{"id":275050951,"uuid":"924917195","full_name":"mikejoh/devstack-on-kvm","owner":"mikejoh","description":"Deploy Devstack on KVM! 🚀","archived":false,"fork":false,"pushed_at":"2025-09-10T19:55:44.000Z","size":13486,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-09-10T23:41:08.177Z","etag":null,"topics":["devstack","openstack"],"latest_commit_sha":null,"homepage":"","language":"HCL","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/mikejoh.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2025-01-30T21:45:27.000Z","updated_at":"2025-09-10T19:55:48.000Z","dependencies_parsed_at":"2025-01-30T22:32:38.094Z","dependency_job_id":"7163e637-6b50-4c2c-b23f-a469469dcd09","html_url":"https://github.com/mikejoh/devstack-on-kvm","commit_stats":null,"previous_names":["mikejoh/devstack-on-kvm"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/mikejoh/devstack-on-kvm","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mikejoh%2Fdevstack-on-kvm","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mikejoh%2Fdevstack-on-kvm/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mikejoh%2Fdevstack-on-kvm/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mikejoh%2Fdevstack-on-kvm/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/mikejoh","download_url":"https://codeload.github.com/mikejoh/devstack-on-kvm/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mikejoh%2Fdevstack-on-kvm/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28473974,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-01-15T22:27:41.514Z","status":"ssl_error","status_checked_at":"2026-01-15T21:54:47.910Z","response_time":62,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.6:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["devstack","openstack"],"created_at":"2025-02-07T19:36:51.102Z","updated_at":"2026-01-15T23:11:11.999Z","avatar_url":"https://github.com/mikejoh.png","language":"HCL","funding_links":[],"categories":[],"sub_categories":[],"readme":"# DevStack on KVM\n\nDeploy [DevStack](https://docs.openstack.org/devstack/latest/) on KVM! 🚀\n\n_For those occasions when you want to test things against OpenStack (APIs) but you don't want to use the one(s) in production._ 😏\n\n## ✅ Pre-requisites\n\n* KVM\n* OpenTofu (the `terraform` fork)\n\n## 🗒️ Important notes\n\n* It'll take some time for the DevStack installation to complete, please run `sudo virsh console devstack01` to follow the installation. Press `Ctrl+5` to exit the console. Please note that we're installing Octavia with everything included (like building of the Octavia worker image), so please be patient.\n* Before starting, stop `ufw` temporarily if it's running locally, i haven't found a good combination of FW rules yet.\n* Info on how to perform configuration customization when installing DevStack check [this](https://github.com/openstack/devstack/blob/master/doc/source/configuration.rst) out.\n\n## 🏃 Getting started\n\n### Provision cluster nodes\n\n1. Change the `devstack.auto.tfvars` to fit your needs!\n2. Run `tofu init`\n3. Run `tofu plan`\n4. Run `tofu apply`\n5. The needed nodes shall be provisioned with everything included for you to start bootstrapping the cluster.\n\nTo see the progress of the DevStack installation, run the following command:\n\n```bash\nsudo virsh console devstack01\n```\n\n### Accessing the DevStack environment\n\n_Remember to start your VMs after a reboot, they'll be shut off by default. Run `sudo virsh start` for each VM!_\n\n#### Browse to the DevStack dashboard\n\nOpen your browser and go to the following URL:\n\n```bash\nhttp://\u003cdevstack01-ip\u003e/\n```\n\nTo find out the IP addresses of the VMs you can run the following using `virsh`:\n\n```bash\nsudo virsh net-dhcp-leases devstack_net\n```\n\nThe DevStack dashboard (Horizon) can be logged into using the following credentials:\n\n```\nUsername: demo\nPassword: secret\n```\n\n#### SSH to the DevStack instance\n\nThis requires that you have [`fzf`](https://github.com/junegunn/fzf) installed.\n\n```bash\nPRIVATE_SSH_KEY=~/.ssh/devstack scripts/ssh.sh\n```\n\n_Make sure you're using the private key that matches the public key added as part of the cluster node provisioning. We're adding a user called `cloud` by default that has the provided public key as one of the `ssh_authorized_keys`._\n\nRun this to generate a new SSH key pair, make sure to point to the public key and it's path in the `tfvars` file:\n\n```\nssh-keygen -t ed25519 -C \"devstack-kvm\" -f ~/.ssh/devstack\n```\n\n#### Using `tmux` and `xpanes` to SSH to all available nodes\n\nDon't forget to inline the private key path below and replace `\u003cPRIVATE_KEY\u003e` before running the command:\n\n```bash\ntmux\n\nsudo virsh net-dhcp-leases devstack_net | tail -n +3 | awk '{print $5 }' | cut -d\"/\" -f1 | xpanes -l ev -c 'ssh -l cloud -i \u003cPRIVATE_KEY\u003e {}'\n```\n\n### How to reach the public IP address range in DevStack\n\nIn DevStack the `public` network will have the following range by default: `172.24.4.0/24`.\n\n1. Enable proxy arp in the `devstack01` VM primary interface:\n\n```\necho 1 \u003e /proc/sys/net/ipv4/conf/ens3/proxy_arp\n```\n\n_Please note that my interface were named `ens3`._\n\n2. Associating floating IPs to e.g. Loadbalancers or instances\n3. Update security groups if needed, i had to allow more in the `default` security group (created automatically)\n4. Update your local routing table, i needed to do the following:\n\n```\nsudo ip route add 172.24.4.0/24 dev virbr1\n```\n\n`virbr1` was the bridge that my `devstack01` VM was connected to as of writing this.\n\n### Cleaning things up\n\nRun `tofu destroy`.\n\n_Note that this destroys all of KVM related objects._\n\n## 🛠️ Troubleshooting\n\n### List all `systemd` units related to DevStack and their statuses\n\n```\nsystemctl list-units 'devstack@*'\n```\n\nFor `systemd` related documentation see [this](https://docs.openstack.org/devstack/latest/systemd.html) link.\n\n### DevStack logs\n\nChecking logs:\n\n```\njournalctl -f -u devstack@*\njournalctl -f -u devstack@\u003cservice\u003e\n```\n\n### Various encountered errors and problems\n\n#### Updating objects in Glance \n\nWhen updating (increasing) the `image_size_total` in Glance via the `openstack` CLI the following where seen in the `g-api` logs:\n\n```\nUnhandled error: oslo_db.exception.DBDeadlock: (pymysql.err.OperationalError) (1205, 'Lock wait timeout exceeded; try restarting transaction')\n```\n\nFixed by restarting the `mysql` service in the DevStack VM:\n\n```\nsystemctl restart mysql\n```\n\n#### Error when listing instances after starting the DevStack VM\n\nHorizon stack trace:\n\n```\nTraceback (most recent call last):\n  File \"/opt/stack/data/venv/lib/python3.10/site-packages/django/core/handlers/exception.py\", line 55, in inner\n    response = get_response(request)\n  File \"/opt/stack/data/venv/lib/python3.10/site-packages/django/core/handlers/base.py\", line 197, in _get_response\n    response = wrapped_callback(request, *callback_args, **callback_kwargs)\n  File \"/opt/stack/horizon/horizon/decorators.py\", line 51, in dec\n    return view_func(request, *args, **kwargs)\n  File \"/opt/stack/horizon/horizon/decorators.py\", line 35, in dec\n    return view_func(request, *args, **kwargs)\n  File \"/opt/stack/horizon/horizon/decorators.py\", line 35, in dec\n    return view_func(request, *args, **kwargs)\n  File \"/opt/stack/horizon/horizon/decorators.py\", line 111, in dec\n    return view_func(request, *args, **kwargs)\n  File \"/opt/stack/horizon/horizon/decorators.py\", line 83, in dec\n    return view_func(request, *args, **kwargs)\n  File \"/opt/stack/data/venv/lib/python3.10/site-packages/django/views/generic/base.py\", line 104, in view\n    return self.dispatch(request, *args, **kwargs)\n  File \"/opt/stack/data/venv/lib/python3.10/site-packages/django/views/generic/base.py\", line 143, in dispatch\n    return handler(request, *args, **kwargs)\n  File \"/opt/stack/horizon/horizon/tables/views.py\", line 222, in get\n    handled = self.construct_tables()\n  File \"/opt/stack/horizon/horizon/tables/views.py\", line 213, in construct_tables\n    handled = self.handle_table(table)\n  File \"/opt/stack/horizon/horizon/tables/views.py\", line 122, in handle_table\n    data = self._get_data_dict()\n  File \"/opt/stack/horizon/horizon/tables/views.py\", line 251, in _get_data_dict\n    self._data = {self.table_class._meta.name: self.get_data()}\n  File \"/opt/stack/horizon/openstack_dashboard/dashboards/project/instances/views.py\", line 156, in get_data\n    futurist_utils.call_functions_parallel(\n  File \"/opt/stack/horizon/openstack_dashboard/utils/futurist_utils.py\", line 50, in call_functions_parallel\n    return tuple(f.result() for f in futures)\n  File \"/opt/stack/horizon/openstack_dashboard/utils/futurist_utils.py\", line 50, in \u003cgenexpr\u003e\n    return tuple(f.result() for f in futures)\n  File \"/usr/lib/python3.10/concurrent/futures/_base.py\", line 451, in result\n    return self.__get_result()\n  File \"/usr/lib/python3.10/concurrent/futures/_base.py\", line 403, in __get_result\n    raise self._exception\n  File \"/opt/stack/data/venv/lib/python3.10/site-packages/futurist/_utils.py\", line 45, in run\n    result = self.fn(*self.args, **self.kwargs)\n  File \"/opt/stack/horizon/openstack_dashboard/dashboards/project/instances/views.py\", line 148, in _get_volumes\n    exceptions.handle(self.request, ignore=True)\n  File \"/opt/stack/horizon/openstack_dashboard/dashboards/project/instances/views.py\", line 145, in _get_volumes\n    volumes = api.cinder.volume_list(self.request)\n  File \"/opt/stack/horizon/openstack_dashboard/api/cinder.py\", line 298, in volume_list\n    volumes, _, __ = volume_list_paged(\n  File \"/opt/stack/horizon/openstack_dashboard/api/cinder.py\", line 337, in volume_list_paged\n    c_client = _cinderclient_with_generic_groups(request)\n  File \"/opt/stack/horizon/openstack_dashboard/api/cinder.py\", line 289, in _cinderclient_with_generic_groups\n    return _cinderclient_with_features(request, 'groups')\n  File \"/opt/stack/horizon/openstack_dashboard/api/cinder.py\", line 271, in _cinderclient_with_features\n    version = get_microversion(request, features)\n  File \"/opt/stack/horizon/openstack_dashboard/api/cinder.py\", line 263, in get_microversion\n    min_ver, max_ver = cinder_client.get_server_version(cinder_url,\n  File \"/opt/stack/data/venv/lib/python3.10/site-packages/cinderclient/client.py\", line 119, in get_server_version\n    data = json.loads(response.text)\n  File \"/usr/lib/python3.10/json/__init__.py\", line 346, in loads\n    return _default_decoder.decode(s)\n  File \"/usr/lib/python3.10/json/decoder.py\", line 337, in decode\n    obj, end = self.raw_decode(s, idx=_w(s, 0).end())\n  File \"/usr/lib/python3.10/json/decoder.py\", line 355, in raw_decode\n    raise JSONDecodeError(\"Expecting value\", s, err.value) from None\n```\n\nWhich pointed at a Cinder related problem. I fixed this by restarting the Cinder API and Cinder Volume services:\n\n```\nsystemctl restart devstack@c-api.service devstack@c-vol.service\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmikejoh%2Fdevstack-on-kvm","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmikejoh%2Fdevstack-on-kvm","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmikejoh%2Fdevstack-on-kvm/lists"}