Using OpenStack

The Basics

If you have not reviewed The OpenStack CLI, please do this before continuing with this section.

Also, if you are looking at OpenStack for the first time, we suggest you review Your First Project before continuing. This will show how to deploy a server, from start to finish.

In this section, we will review many common OpenStack CLI commands that a sysadmin will use to manage the Genesis Public Cloud.

Note that any resources you create in this section will be metered every 30-seconds and billed accordingly.

For any script that we include in your jump host, help will be provided if you run the script without any parameters.

Domain-level commands

When managing users and projects, you will need to use your Domain Admin account. However, you will find that when running commands in OpenStack that you will need to specify the domain on the command-line, such as:

openstack user list --domain $OS_DOMAIN_ID

Without the domain parameter, OpenStack assumes that you want to list all users in the cloud, which you have no permissions for. So, you need to restrict the scope of the list by specifying the domain.

In the scripts we have provided in your jump host, we have included the domain parameter for domain-related operations, so it may be easiest to use our scripts, but if you run into a permissions error, such as:

You are not authorized to perform the requested action: identity:list_users.

The reason is that the domain was not specified.

Project quotas

OpenStack controls quotas at a per-project level, with more advanced quotas being developed, called Unified Limits (see Unified Limits).

Genesis has reasonable defaults for per project quotas, which can be expanded upon request with justification.

We have also made it easy to view the quotas using a script on your jump host:

./quotas.sh

The following is a short list of quotas we have created per project:

Quota Value
Max backup storage 100TiB
Max backup count 10000
Max processor cores 16
Max memory 256GiB
Max Floating IPs 5
Max networks 10
Max ports 500
Max security groups 50
Max security group rules 200
Max volume count 20
Max volume snapshots 20
Max volume storage 20TiB
Max volume size 4TiB
Max VM count 20
Max key pairs 100

Creating users

Users are assigned to your domain, so you must use your Domain Admin account to create users.

OpenStack uses roles to determine the permissions for users who belong to the role. Genesis currently supports a “domain_admin” role and “member” role, the latter is used for all project admins. Soon, we will support other roles that allow finer control over a project’s resources.

Users who belong to the “member” role are “Project Admins” and have full administrative privilege over a project, which means they can view, create, update, and delete resources within a project. Project Admins do not have the privilege of viewing other users, nor their role assignments, nor can create users. These functions are reserved for the Domain Admin.

To list the current users in your account, we have created a script in your jump host called:

./list_users.sh

We suggest you name users in such a way that their project is included in the name. For example, if you create a project called “ABC” for a user named “John”, you may consider naming the user “ABC_John” or “ABC.John”. Note that usernames are case insensitive, so “abc.john” is the same as “ABC.John”.

To simplify the process of creating a user, we have included a script in your jump host called:

./create_project_admin.sh <project name> <project admin username>

This script performs a few things:

  • Creates the user
  • Assigns the “member” role to the user for the project you specify
  • Creates an openrc file for this user
  • Tests object storage connectivity to show that authentication is working ok

We have also included a script to delete a user called:

./delete_project_admin.sh <project admin user name or id>

Creating projects

You can create projects in your domain, which are the same as Virtual Private Clouds (in AWS terms). Projects have complete isolation of VMs, routers, networks, IPs, etc., giving you the ability to create a tenant within your environment.

Projects are perfect for creating your own client accounts, in case you are a hosting company or MSP that requires complete separation of resources between clients. Projects also provide an easy way to delete resources that are specific to one of your clients.

We create an invoice per project, in case you need separate bills for each of your clients.

Users can also be created specifically for a project, in case you need to give one of your clients access to the OpenStack project (or projects) for their account - allowing them to manage their own resources.

Creating a project is simple. We have included a script in your jump host for this:

./deploy_project.sh <project name> <project admin username>

The project admin username can be an existing user. If the user does not exist, the script will create the user. In both cases, an openrc file for this user for this project.

Creating test servers

In case you want to create a number of servers quickly, we have included a script in your jump host to assist. Be sure to login as a project admin first.

CentOS test servers:

./deploy_test_centos_servers.sh <public key file> <create floating ips yes/no> <server start index> <server count> <boot volume size in GiB>

Windows test servers:

./deploy_test_windows_servers.sh <public key file> <create floating ips yes/no> <server start index> <server count> <boot volume size in GiB>

For example, to create one CentOS server that uses the jump host’s public key, assigns a floating IP to the machine, starting with an index of 001 on the end of the name of the server, and a boot volume size of 50GiB, run the following:

./deploy_test_centos_servers.sh .ssh/id_rsa.pub yes 1 1 50

Or, to create a Windows Server 2019 server:

./deploy_test_windows_servers.sh .ssh/id_rsa.pub yes 1 1 50

Both scripts provide step-by-step feedback and end with instructions on how to connect to the server(s).

We have also included scripts to destroy the test servers, including:

CentOS test servers:

./destroy_test_centos_servers.sh <server start index> <server count> --yes-i-really-really-mean-it

Windows test servers:

./destroy_test_windows_servers.sh <server start index> <server count> --yes-i-really-really-mean-it

Creating a volume

Genesis includes two compute flavors, one that includes a boot drive at a discounted price, and one that does not. The former is fixed in size, unless you resize the server with a flavor that includes a larger drive. The latter is designed for volume-based boot drives.

This section describes how to create volumes for the latter scenario.

Genesis has many options for volume types including Ceph-based storage and high-speed, low-latency NVMe SSD storage.

Ceph is a distributed storage platform with numerous abstractions to scale out storage across server nodes. This has the advantage of easy scale-out, expansion, and re-distribution of storage without the user knowing, or caring, that all of this is happening behind the scenes. The downside is that the abstractions add latency and require significant compute resources to process I/O commands.

Genesis uses Ceph for a number of volume types, but we found that Ceph is simply too slow for some workloads and we needed to have a high-speed, low-latency solution. We use a commercial product for the latter, which gives us the benefit of providing a wide array of volume types from archive storage up to extremely high performance storage. In addition, we also provide provisioned IOPS storage, which guarantees storage performance.

Note that we have two images for each operating system, one with “_raw” at the end. Use of the _raw image is best used with Ceph-based volume types (see the next section for information about volume types), where Ceph will create an inline snapshot of the image during server deployment, providing for a much faster deployment.

Images without _raw on the end are qcow2 images, which are compressed. These are best used for non-Ceph volume types, where the image must be copied in its entirety to the target storage. By using a qcow2 image, the copy process is much faster.

Viewing the list of volume types is easy with a script that we included in your jump host:

./list_volume_types.sh

Creating a volume is quite simple, but requires that we make a few decisions:

  • Size
  • Volume type
  • Imaging - pick one * Copy an operating system image from our library * Copy another volume * Copy a snapshot of another volume * Create an empty volume (no image)
  • Specify whether this is a bootable volume
  • Specify whether the volume is read-only or not
  • Name of the volume

You can view how these parameters are provided to OpenStack by viewing the parameter list for the volume create command:

openstack volume create

usage: openstack volume create [-h] [-f {json,shell,table,value,yaml}]
                               [-c COLUMN] [--noindent] [--prefix PREFIX]
                               [--max-width <integer>] [--fit-width]
                               [--print-empty] [--size <size>]
                               [--type <volume-type>]
                               [--image <image> | --snapshot <snapshot> | --source <volume>]
                               [--description <description>]
                               [--availability-zone <availability-zone>]
                               [--consistency-group consistency-group>]
                               [--property <key=value>] [--hint <key=value>]
                               [--bootable | --non-bootable]
                               [--read-only | --read-write]
                               <name>

The following is an example command to create a 100GiB volume named “CentOSVolume”, with a high-speed volume type of gp3, that will be used as a boot volume for a VM with a CentOS 7 image copied to the volume:

openstack volume create \
  --size 100 \
  --type gp3 \
  --image centos-7-x86_64-GenericCloud-1905 \
  --bootable \
  CentOSVolume

Note that the volume create operation, like many OpenStack operations, is asynchronous, meaning that the command is queued for execution, and runs in the background, returning you to a bash prompt after queueing the command.

To view the status of the command, run:

openstack volume show CentOSVolume

You can filter this command to only show the status value using additional flags:

openstack volume show CentOSVolume -f value -c status

Once the status is “available”, the volume is ready for use.

To delete a volume, use:

openstack volume delete CentOSVolume

To list all of your volumes in your project, run the following script we provided in your jump host:

./list_volumes.sh

Creating a key pair

When we built your jump host, we ran “ssh-keygen” for the “centos” user, which generated a public/private key pair in the .ssh directory under your home directory. Two files were created, “id_rsa” (your private key) and “id_rsa.pub” (your public key).

You never share your private key.

Your public key is used in servers your deploy, so you can login using passwordless authentication. This provides a much safer login process than username/password combinations, mostly due to the fact that the keys are extremely lengthy (4096 bits to be exact). This makes guessing impossible, but it does require that you take care to keep your private key safe. A key can be created with a passphrase, which encrypts your private key, but this requires you enter the passphrase each time the private key is used, which is not practical for automated (scripted) operations. So, the key we created for you does not have a passphrase.

For OpenStack to inject the public key into an image when creating a server, it requires this key be stored in its vault. This is done by using the following command:

openstack keypair create --public-key <file> MyPublicKey

Replace “MyPublicKey” with something suitable, and replace <file> with the path to your public key (usually “.ssh/id_rsa.pub”).

Creating a server

Genesis includes two flavors, one that includes a boot drive at a discounted price, and one that does not. The former is fixed in size, unless you resize the server with a flavor that includes a larger drive. The latter is designed for volume-based boot drives.

We will provide an example for each scenario.

NOTE that we assume you have read Your First Project and understand how to create networks, subnets, routers, and security groups.

First, Genesis has many option for compute flavors including shared core, dedicated core, volume-based flavors, and flavors that include a boot drive.

Flavors with boot drives

We will first create a server using a flavor that includes a boot drive, but we need to make some decisions beforehand:

  • Flavor
  • Operating System image to use
  • Key pair in OpenStack’s vault
  • Security groups (must create beforehand)
  • Network (must create beforehand)
  • Name of server

We will use the t5sd.2xlarge flavor to create a server using the following command:

openstack server create \
  --flavor t5sd.2xlarge \
  --image centos-7-x86_64-GenericCloud-1905_raw \
  --key-name userkey \
  --security-group AllowSSHInbound \
  --network GHSProjectNetwork001 \
  MyTestServer

Note that the server create operation, like many OpenStack operations, is asynchronous, meaning that the command is queued for execution, and runs in the background, returning you to a bash prompt after queueing the command.

To view the status of the command, run:

openstack server show MyTestServer

You can filter this command to only show the status value using additional flags:

openstack server show MyTestServer -f value -c status

Once the status is “ACTIVE”, the server is running.

You may want to view the console of the server at this point, to be sure it booted ok. Using some scripts we have included with your jump host, you can either review the text produced on the console using:

./boot_log.sh <server name or id>

or view a graphical interface of the console:

./console.sh <server name or id>

Note that the console.sh command generates a temporary URL (that expires) that you can browse to, providing an interactive view of the console of the server.

To delete a server, use:

openstack server delete MyTestServer

Note

For a server created with a flavor that has a built-in boot drive, when deleting the server, this also deletes the boot drive! This is not the case for volume-based flavors.

When deleting a flavor without a built-in boot drive, a volume is attached to the server at boot time. When the server is deleted, the volume is simply detached, and not deleted.

Flavors with no boot drives

Next, we will create a server from a flavor that has no boot drive, and instead mount a volume-based boot drive.

We will use the volume created in the Creating a volume section.

The decisions we need to make are similar to using flavors with built-in boot drives, but we no longer have to choose an image, since that was already done during the creation of the boot volume:

  • Flavor
  • Key pair in OpenStack’s vault
  • Security groups (must create beforehand)
  • Network (must create beforehand)
  • Name of server

We will use the t5sd.2xlarge flavor to create a server using the following command:

openstack server create \
  --flavor t5sd.2xlarge \
  --volume CentOSVolume \
  --key-name userkey \
  --security-group AllowSSHInbound \
  --network GHSProjectNetwork001 \
  MyTestServer

Note that the server create operation, like many OpenStack operations, is asynchronous, meaning that the command is queued for execution, and runs in the background, returning you to a bash prompt after queueing the command.

To view the status of the command, run:

openstack server show MyTestServer

You can filter this command to only show the status value using additional flags:

openstack server show MyTestServer -f value -c status

Once the status is “ACTIVE”, the server is running.

You may want to view the console of the server at this point, to be sure it booted ok. Using some scripts we have included with your jump host, you can either review the text produced on the console using:

./boot_log.sh <server name or id>

or view a graphical interface of the console:

./console.sh <server name or id>

Note that the console.sh command generates a temporary URL (that expires) that you can browse to, providing an interactive view of the console of the server.

To delete a server, use:

openstack server delete MyTestServer

Note

For a server created with a flavor that has a built-in boot drive, when deleting the server, this also deletes the boot drive! This is not the case for volume-based flavors.

When deleting a flavor without a built-in boot drive, a volume is attached to the server at boot time. When the server is deleted, the volume is simply detached, and not deleted.

Creating a floating IP

Please review the section Adding a floating IP.

Creating an image

Please review the section Choosing an image for information about existing images.

You may want to create your own image. You may have a virtual machine image that you want to import, for example, such as the latest Ubuntu Desktop image.

First, download the image to your jump host, or another machine that has been provisioned with the OpenStack CLI tools using our instructions Installing the OpenStack CLI.

For example, you can use the following command on your jump host to retrieve the Ubuntu 19.10 desktop image:

wget https://cloud-images.ubuntu.com/focal/current/focal-server-cloudimg-amd64.img

Note

Downloading a large image to your jump host may take a while since we use a t5sd.large flavor, which is bandwidth limited to 25Mbps, or about 3MiB/sec after HTTPS overhead. The same bandwidth limit will be in place for uploading the image to OpenStack.

Once downloaded, you can upload the image to your project using the following command on your jump host:

openstack image create \
  --disk-format qcow2 \
  --container-format=bare \
  --property architecture=x86_64 \
  --property "os_distro=ubuntu" \
  --property "os_type=linux" \
  --file ./focal-server-cloudimg-amd64.img \
  focal-server-cloudimg-amd64

Note

This command runs synchronously, and so does not exit until the entire image has been uploaded. For large images, this can take a while.

Also note that the property values must be set correctly, otherwise the server with this image will not launch.

Valid values for os_distro and os_type are available on the OpenStack site.

This image can now be used to create a server such as with this command:

openstack server create \
  --flavor t5sd.2xlarge \
  --image focal-server-cloudimg-amd64 \
  --key-name userkey \
  --security-group AllowSSHInbound \
  --network GHSProjectNetwork001 \
  MyNewUbuntuServer

Resizing a server

When a server is deployed with a flavor that has a built-in boot drive, the resize operation requires you resize to a flavor with the same-sized or larger-sized boot drive. Resizing to a smaller boot drive is not supported. Note that this will also trigger Cloud-Init to resize the filesystem on the boot drive, so no user intervention is required to use the extra space if a flavor with a larger boot drive is chosen.

When a server is deployed wiht a flavor that has no boot drive, requiring a volume-based boot drive, the server can be assigned any flavor that does not have a built-in boot drive, regardless of whether the flavor is smaller or larger, since the volume is simply detached, the flavor changed, and the volume re-attached before booting.

Resizing will shut down a server, change the flavor, and boot the server, so a short amount of downtime is required (sometimes less than 1 minute).

To perform a resize, simply run the following command:

openstack server resize --flavor <new flavor> <server name or id>

Creating port forwarding rules

Genesis Public Cloud supports port forwarding with floating IPs so you can publish multiple servers’ services behind a single public IP on different ports. This saves on the cost of floating IPs, but also provides for efficient use of floating IPs where ports can be specified.

For example, if two servers were created, Server A and Server B, where Server A is a web server and Server B is a mail server, a single floating IP can be used for both servers, such as:

Floating IP xxx.xxx.xxx.xxx TCP Port 80 -> Server A Private IP TCP Port 80
Floating IP xxx.xxx.xxx.xxx TCP Port 443 -> Server A Private IP TCP Port 443
Floating IP xxx.xxx.xxx.xxx TCP Port 25 -> Server B Private IP TCP Port 80

Port forwarding can also be used to redirect a port. For example, you may want to publish your RDP connection on TCP Port 65550 instead of the standard TCP Port 3389, such as:

Floating IP xxx.xxx.xxx.xxx TCP Port 65550 -> Server A Private IP TCP Port 3389

First, you need to create your floating IP, which cannot be used for 1:1 NAT, so do not use “openstack server add floating ip” for any servers with this floating IP.

You will need to gather the following information:

  • Private IP address of the server(s)
  • Internal port of the service on the server
  • External port of the service to be published with the floating IP
  • Protocol to forward (tcp or udp)
  • The OpenStack port of the server where the Private IP is located

We have included the following script with your jump host to assist with the process of adding a port forwarding rule:

./server_add_port_forwarding_rule.sh <server> <floating ip> <internal port> <external port> <protocol> <add security group yes/no>

A security group to allow traffic inbound to the external port is required before traffic will be allowed to enter the port forwarding rule. The script above will do this for you if you specify “yes” for the <add security group yes/no> parameter.

Creating backups

It is always important to backup your VMs. There are two methods for doing this, depending on whether you deployed a server with a flavor that has a built-in boot drive or a flavor with no built-in boot drive, which has a volume (or volumes) attached.

Flavors with boot drives

For VMs deployed with a flavor that has a built-in drive, backups are created using the following command:

openstack server image create --name <image name of backup> <server>

This creates an image that contains the boot drive’s contents. It does NOT include any details about the VM itself, such as which flavor the server was configured.

Note that backups are performed to image storage, which can get pricey for larger VMs.

Flavors with no boot drives

For VMs deployed with a flavor that does not have a built-in drive, backups are created of the volumes of the server. We have created a script that is included with your jump host that performs a backup of each volume in a project, given an openrc file for the project:

./backup_project_volumes.sh <openrc file for project admin>

This script first dumps all of the server and volume information to a log file, and then creates a full backup of each volume, waiting 10 minutes between starting each backup. This information can be used to reconstruct the VMs using the flavor information, which volumes were attached to the VMs, etc.

You can create a cron job on your jump host that runs this script for each project.

Volume backups are stored in the object storage for the respective project, and thus is a very inexpensive solution.

To view the status of your volume backups, use the following command:

openstack volume backup list

Alternatives

Alternatively, you can use Genesis Backup, which is an agent-based backup solution we offer. We especially recommend Genesis Backup for Windows-based servers.

For Linux, Genesis Backup can perform file-level backups, but not image level backups. You can also use rsync, rclone, or various other file synchronization tools.

Creating load balancers

Load balancers consist of two VMs in a high-availability configuration, deployed on physically separate systems. Each load balancer can have multiple listeners, which respond to traffic sent to a specific TCP or UDP port, so you can share a single load balancer for many services, unless you have extremely high-performance and scalable requirements and wish to create multiple load balancers, one for each service, and potentially with DNS round-robin between the balancers.

Listeners direct traffic to a server pool. A server pool can have zero or more servers (pool members). The pool defines how requests will be directed to its members and how session persistence can be configured. Multiple listeners can direct requests to the same pool.

A health monitor is used to perform health checks on each member of a server pool to determine whether a member is directed requests or not. Requests are typically HTTP requests, where the monitor expects a specific HTTP response code to be considered “healthy”. Pings, TCP connections, and TLS-HELLO messages can also be used to perform the check.

Load balancers can be complex to configure, since there are a large number of options, so we have created a few scripts to help. In your jump host, we included the following script to ease with the deployment of a basic load balancer:

./deploy_basic_load_balanced_servers.sh <new project name or id> --yes-i-really-really-mean-it

This script creates a new project, two servers, a simple HTTP load balancer, and uses Ansible to updates each server as well as install Apache, configure Apache, and create a simple site with a single file. This script can be copied and modified for more sophisticated configurations.

The deploy_basic_load_balanced_servers.sh script uses some other scripts to accomplish its task, including:

./deploy_loadbalancer_http.sh <lb name> <subnet_id> <VM001 ip address> <VM002 ip address>

This script performs the work of creating the HTTP load balancer. We also have included a script that can be used to create an HTTPS load balancer, where the front-end services HTTPS requests with an installed SSL certificate, and directs HTTP requests on the back-end to web servers:

./deploy_loadbalancer_https.sh <lb name> <subnet_id> <VM001 ip address> <VM002 ip address> <certificate_name> <certificate_p12_file>

If you have any issues or questions, please Contact Us.

Creating IPSEC VPNs

Connecting networks is very simple with OpenStack’s IPSEC VPN commands. We included a script in your jump host that makes it even easier to deploy:

./deploy_ipsec_vpn.sh <router> <subnet> <remote network> <peer ip> <secret>

The router and subnet parameters are a OpenStack router and OpenStack subnet, whereas remote network, peer ip, and secret are strings. The remote network will be something like “192.168.100.0/24”, the peer ip will be the public IP of the remote VPN gateway, and the secret is a long string that is shared between the remote VPN gateway and this VPN.

Appropriate security groups are required for the VMs to communicate with the remote systems, and can be tuned to only allow communications between local and remote private networks, avoiding any public communications.

Using object storage

Object storage has many uses, but is typically used for bulk storage of a large number of files that can be referenced by a single path, similar to a key/value store, but with large files.

The Genesis Public Cloud object storage supports both S3 and Swift APIs, and thus can be used by most software that can use these APIs. This includes many backup systems, such as Veeam, CommVault, CloudBerry (now MSP360), etc.

There is also software that presents object storage as a mountable filesystem or even a drive letter in Windows. Cloudberry Explorer, S3 Browser, Cyberduck, are examples.

There are also free CLI options, including the OpenStack CLI and the aws command line tool. For example, simply specify our endpoint:

aws --endpoint-url https://s3-us-central-1.genesishosting.com/ s3 cp <source file> s3://<bucketname/<filename>

Every Genesis Public Cloud project has its own object storage repository, where many containers (buckets in AWS terms) can be created. A container is like a folder in a filesystem. Container naming is unique to each project, not to the entire cloud.

To view the properties of a repository (an object store account):

openstack object store account show

To create a container, use the following command:

openstack container create <container name>

To view the list of containers:

openstack container list

To view the number of objects in a container:

openstack container show <container name>

To upload an object to a container:

openstack object create <container name> <filename> [<filename> ...]

To download an object from a container:

openstack object save [--file <filename>] <container name> <object>

S3 compatibility

If you are using an S3 client, you can easily create multiple access/secret S3 credential pairs for a project by using the following command:

openstack ec2 credentials create

The access and secret keys will be provided, which can also be reviewed using:

openstack ec2 credentials list

Note

The EC2 credentials are created per project admin, not at the project level. One project admin cannot view the credentials for another project admin.

However, this does not indicate that each project admin receives its own object storage account. All project admins can view/update/delete items within the project’s object storage account.

S3 Bucket Policies are supported, and can provide access restrictions to buckets.

We will be soon be writing documentation on how to utilize S3 features in real world circumstances.

The endpoint for your S3 client will be in the form:

https://s3-<region>.genesishosting.com/

For our us-central-1 region, the endpoint will be:

https://s3-us-central-1.genesishosting.com/

Signature version 4 is required and native multipart uploads are supported.

Genesis uses the Nautilus version of Ceph and Ceph’s RadosGW for our object storage platform and gateways. Compatibility with the S3 API can be found on the Ceph S3 API page.

Creating DNS records

Forward and reverse authoritative DNS services are included with your Genesis Public Cloud account.

Lets assume you have a new domain, mydomain.com, and wish to point this to your resources at Genesis. You will first need to create the zone in OpenStack using:

zone_id=$(zone create --email email@mydomain.com mydomain.com.)

Be sure to include the trailing dot at the end of the domain.

View the default records for this zone using:

openstack recordset list $zone_id

You will need a floating IP address for your resource, which can then be used to create DNS records.

For example, you may want a CNAME record for the www version of the domain (using the $zone_id variable above):

openstack recordset create --type CNAME --record mydomain.com. $zone_id www

And then an A record:

openstack recordset create --type A --record <floating ip> $zone_id mydomain.com.

Finally, point your domain’s authoritative DNS server at the Genesis Public Cloud DNS servers:

gdns1.genesishosting.com
gdns2.genesishosting.com
gdns3.genesishosting.com
gdns4.genesishosting.com
gdns5.genesishosting.com

You can perform a simple test using the “host” command against one of the above DNS servers, such as:

host mydomain.com gdns1.genesishosting.com

Now, let’s create a Reverse DNS record. First, we need to retrieve the UUID of the floating IP:

fip_id=$(openstack floating ip show <floating ip> -f value -c id)

And then set the PTR record for the floating ip using:

openstack ptr record set us-central-1:$fip_id mydomain.com.

You may want to set the TTL of the PTR record to something other than the default (1 hour). Simply add the –ttl parameter. For example, to set the TTL to 5 minutes, run the following:

openstack ptr record unset us-central-1:$fip_id
openstack ptr record set --ttl 300 us-central-1:$fip_id mydomain.com.

To view all reverse DNS records for the project:

openstack ptr record list