Nutanix Calm DSL – Project Environments

Nutanix Calm DSL - Project Environments

Table of Contents

In the recently released Nutanix Calm DSL Lab, a ton of various Calm DSL topics were covered. Before diving into the content for this article, I strongly recommend checking out the lab, as it is a great way to get started with the Calm DSL.

One of the topics in the lab was Calm Projects. In Nutanix Calm, the project construct defines various environment-specific settings. For example:

  • Permissions e.g. user accounts and groups that can deploy a marketplace application.
  • The networks that can be used when deploying an application.
  • Default VM specifications and deployment options – vCPUs, vRAM, storage, base images, Cloud-Init or Sysprep specs.
  • Credentials.

At the time of writing the Calm DSL lab, a Calm project’s accompanying environment couldn’t yet be managed by the Calm DSL – that has now changed. In addition to the Calm DSL lab, I also published an article in latest August that outlined how to create Calm projects using the Calm DSL CLI tools. To ensure today’s content has some context, I’ll re-use some of that content here.

Quick Recap – Project Files

When using the Calm DSL CLI to create a project, an accompanying project file is required. This is a Python file that describes various project settings e.g. the cluster name, groups and users to assign to the new project. For reference, the project file below is what was used in the Nutanix Calm DSL – Managing Projects article.

from calm.dsl.builtins import Project
from calm.dsl.builtins import Provider, Ref


ACCOUNT = "NTNX_LOCAL_AZ"
SUBNET = "vlan.0"
CLUSTER = "Galactica"
USER = "jane.doe@ntnx.local"
GROUP = "cn=sspadmins,cn=users,dc=ntnx,dc=local"
VCPUS = 1
STORAGE = 2  # GiB
MEMORY = 1  # GiB


class TestDemoProject(Project):
    """Project created by the Calm DSL"""

    providers = [
        Provider.Ntnx(
            account=Ref.Account(ACCOUNT),
            subnets=[Ref.Subnet(name=SUBNET, cluster=CLUSTER)],
        ),
    ]

    users = [
        Ref.User(name=USER),
    ]

    groups = [Ref.Group(name=GROUP)]

    quotas = {"vcpus": VCPUS, "storage": STORAGE, "memory": MEMORY}

Also for reference, the command used to create the project using that file was as follows.

calm create project --file test_project.py --name test_project_cr --description "project created by the calm dsl"

You’ll notice, however, that there is no mention of the project environment in that project file. This means some Calm features can’t be used with that project. To ensure all features are available, the project needs to have environment settings configured. Before doing that, let’s look at what happens when a project is created with the project file above.

Project created with no Environment
The project has no credentials
Lastly, the project has no network adapters for use when connecting to deployed VMs

None of these things seem particularly major in isolation, since a Calm blueprint can specify those settings for us. However, certain features e.g. Marketplace Blueprint deployments, will sometimes require these settings to exist before the application can be deployed. Without those settings configured via a project’s environment, a true end-to-end infrastructure deployment would require some manual intervention.

Let’s look at the project file changes so the environment can be configured at the same time.

Calm DSL Project File with Environment Configuration

Before continuing, it’s worth noting that the required changes to configure a project’s environment may seem significant. In addition to the top-level projects settings like name, groups and users, we will also specify:

  • 1x default credential. This can be any credential supported by Nutanix Calm, i.e. username and password or SSH key pair authentication.
  • 1x VM specification that will be used as the default when required.
  • 1x network adapter that will be connected to an AHV subnet specified by the project file.
  • The required Calm DSL Python imports that enable support of these settings.

The complete project file now looks like this. Below this file I’ll briefly highlight the changes, although each one has been indicated by a comment within the file itself.

from calm.dsl.builtins import Project
from calm.dsl.builtins import Provider, Ref

# read_local_file has been added as an additional import
from calm.dsl.builtins import read_local_file

# basic_cred has been added as an additional import
from calm.dsl.builtins import basic_cred

# Environment has been added as an additional import
from calm.dsl.builtins import Environment

# Substrate has been added as an additional import
from calm.dsl.builtins import Substrate

# readiness_probe has been added as an additional import
from calm.dsl.builtins import readiness_probe

# AhvVm has been added as an additional import
from calm.dsl.builtins import AhvVm

# AhvVmResources has been added as an additional import
from calm.dsl.builtins import AhvVmResources

# AhvVmDisk has been added as an additional import
from calm.dsl.builtins import AhvVmDisk

# AhvVmGC has been added as an additional import
from calm.dsl.builtins import AhvVmGC

# AhvVmNic has been added as an additional import
from calm.dsl.builtins import AhvVmNic

# this section has been added to allow environment configuration
# specifically, this creates a Calm credential
# at least one credential is mandatory when configuring a project environment
CENTOS_KEY = read_local_file("keys/centos")
CENTOS_PUBLIC_KEY = read_local_file("keys/centos_pub")

Centos = basic_cred("centos", CENTOS_KEY, name="Centos", type="KEY", default=True)


# when configuring a project environment, a default VM configuration must be provided
# here we are defining both an AHV VM and the Substrate that will use it
# please note the "centos7-cloudinit" image and "vlan.0" network referenced by the spec is cluster-specific
# you will need to alter those settings to match your environment
class MyAhvLinuxVmResources(AhvVmResources):

    memory = 4
    vCPUs = 2
    cores_per_vCPU = 1
    disks = [
        AhvVmDisk.Disk.Scsi.cloneFromImageService("centos7-cloudinit", bootable=True),
    ]
    nics = [AhvVmNic("vlan.0")]

    guest_customization = AhvVmGC.CloudInit(
        config={
            "users": [
                {
                    "name": "centos",
                    "ssh-authorized-keys": [CENTOS_PUBLIC_KEY],
                    "sudo": ["ALL=(ALL) NOPASSWD:ALL"],
                }
            ]
        }
    )


class MyAhvLinuxVm(AhvVm):

    resources = MyAhvLinuxVmResources
    # categories may need to be altered to match your environment
    categories = {"AppFamily": "Backup", "AppType": "Default"}


class AhvVmSubstrate(Substrate):
    """AHV VM config given by reading a spec file"""

    provider_spec = MyAhvLinuxVm
    readiness_probe = readiness_probe(disabled=True)


ACCOUNT = "NTNX_LOCAL_AZ"
SUBNET = "vlan.0"
CLUSTER = "Galactica"
USER = "jane.doe@ntnx.local"
GROUP = "cn=sspadmins,cn=users,dc=ntnx,dc=local"
VCPUS = 1
STORAGE = 2  # GiB
MEMORY = 1  # GiB


# this class has been added and links together both our previously-defined
# credentials and VM configuration
class ProjEnvironment(Environment):
    substrates = [AhvVmSubstrate]
    credentials = [Centos]


class TestDemoProject(Project):
    """Project created by the Calm DSL"""

    providers = [
        Provider.Ntnx(
            account=Ref.Account(ACCOUNT),
            subnets=[Ref.Subnet(name=SUBNET, cluster=CLUSTER)],
        ),
    ]

    users = [
        Ref.User(name=USER),
    ]

    groups = [Ref.Group(name=GROUP)]

    quotas = {"vcpus": VCPUS, "storage": STORAGE, "memory": MEMORY}

    # here we are using our previously-defined Environment instance
    # as the environment configuration for the new project
    envs = [ProjEnvironment]

The changes are as follows.

  • Required imports have been specified at the start of the file. I’ve spaced them out onto separate lines but only for the sake of readability.
  • 1x SSH key pair credential named Centos has been created, utilising two local files:
    • ~/.calm/.local/keys/centos
    • ~/.calm/.local/keys/centos_pub
  • 1x VM spec named MyAhvLinuxVmResources has been added.
  • 1x AHV VM named MyAhvLinuxVm has been added.
  • 1x Calm substrate named AhvVmSubstrate has been added (this is what the project will use when configuring the environment).
  • An instance of Environment has been added, named ProjEnvironment.
  • The ProjEnvironment instance has been added to the project definition.

To create the project using this new project file, the command is exactly the same as before. The only changes I’ve made are to the name and description of the project, and only so they are easily differentiated in my development environment.

calm create project --file test_project_with_env.py --name test_project_cr_with_env --description "project with environment created by the calm dsl"

The results look like this:

Creating a Calm project with configured environment, using the Nutanix Calm DSL

Lastly, if we now look at Prism Central and open the project settings within Calm, the environment warning is gone, the Centos credential is ready to be used and the default VM spec is present.

No environment warning showing
“Centos” credential ready to be used
Part of the default VM specification (full spec cut to reduce screenshot size)

Wrapping Up

This process may seem somewhat minor on its own, especially considering it is something those with Nutanix Calm experience will be familiar with.

However, combining this process with the ability to create and deploy blueprints, manage marketplace items and assign permissions (more on that in the next article) means this is just the next step towards configuring an entire infrastructure using automation processes.

Hopefully this information was interesting and useful. Thanks for reading and have a great day! 🙂

© 2024 Nutanix, Inc. All rights reserved. Nutanix, the Nutanix logo and all Nutanix product, feature and service names mentioned herein are registered trademarks or trademarks of Nutanix, Inc. in the United States and other countries. Other brand names mentioned herein are for identification purposes only and may be the trademarks of their respective holder(s). This post may contain links to external websites that are not part of Nutanix.com. Nutanix does not control these sites and disclaims all responsibility for the content or accuracy of any external site. Our decision to link to an external site should not be considered an endorsement of any content on such a site. Certain information contained in this post may relate to or be based on studies, publications, surveys and other data obtained from third-party sources and our own internal estimates and research. While we believe these third-party studies, publications, surveys and other data are reliable as of the date of this post, they have not independently verified, and we make no representation as to the adequacy, fairness, accuracy, or completeness of any information obtained from third-party sources.