Skip to content

Commit

Permalink
Squashed commit of the following:
Browse files Browse the repository at this point in the history
commit 351b5c0
Author: abhijeetkaurav1st <abhijeet.kaurav@nutanix.com>
Date:   Thu Dec 16 14:14:30 2021 +0530

    Minor fixes for undefined attributes at profile model for version setup < 3.3.0.

commit 7ea5982
Author: Abhijeet Singh Kaurav <abhijeet.kaurav@nutanix.com>
Date:   Wed Dec 15 19:03:06 2021 +0530

    Added support for get_runbook_action and macro in ahv-disk-images (#206)

    * Allow macro in image names

    * Adding example that shows macor usage in vm-disk-image and vm-nic

    * Adding helper get_runbook_action for getting action out of imported runbook

    * Adding exsiting machine example and minor improvements

    * minor fixes in models

    * Fixing runbook examples

commit bc9dc6a
Author: Abhijeet Singh Kaurav <abhijeet.kaurav@nutanix.com>
Date:   Mon Dec 13 20:25:01 2021 +0530

    Use ASCENDING order for list_all query (#205)

    fixes #200
  • Loading branch information
abhijeetkaurav1st committed Jan 11, 2022
1 parent 6e59fa6 commit 67c0d97
Show file tree
Hide file tree
Showing 14 changed files with 353 additions and 30 deletions.
9 changes: 8 additions & 1 deletion calm/dsl/api/resource.py
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,14 @@ def list_all(self, api_limit=250):
final_list = []
offset = 0
while True:
response, err = self.list(params={"length": api_limit, "offset": offset})
response, err = self.list(
params={
"length": api_limit,
"offset": offset,
"sort_attribute": "_created_timestamp_usecs_",
"sort_order": "ASCENDING",
}
)
if not err:
response = response.json()
else:
Expand Down
3 changes: 2 additions & 1 deletion calm/dsl/builtins/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
from .models.metadata import Metadata, MetadataType
from .models.credential import basic_cred, secret_cred, CredentialType
from .models.variable import Variable, setvar, CalmVariable, VariableType
from .models.action import action, parallel, ActionType
from .models.action import action, parallel, ActionType, get_runbook_action

from .models.task import Task, CalmTask, TaskType

Expand Down Expand Up @@ -110,6 +110,7 @@
"TaskType",
"action",
"ActionType",
"get_runbook_action",
"parallel",
"Port",
"port",
Expand Down
78 changes: 78 additions & 0 deletions calm/dsl/builtins/models/action.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,15 @@
import inspect
import sys

from .entity import EntityType, Entity
from .validator import PropertyValidator
from .task import create_call_rb
from .runbook import runbook, runbook_create

from calm.dsl.log import get_logging_handle

LOG = get_logging_handle(__name__)

# Action - Since action, runbook and DAG task are heavily coupled together,
# the action type behaves as all three.

Expand Down Expand Up @@ -47,6 +52,23 @@ class action(runbook):
action descriptor
"""

def __init__(self, user_func, task_target_mapping={}, imported_action=False):
"""
A decorator for generating runbooks from a function definition.
Args:
user_func (function): User defined function
task_target_mapping (dict): Mapping for task's target. Used for imported runboosk in blueprint
imported_action (boolean): True if runbook imported as action in blueprint
Returns:
(Runbook): Runbook class
"""

super(action, self).__init__(user_func)

# Will be used in runbooks imported to blueprint actions
self.task_target_mapping = task_target_mapping
self.imported_action = imported_action

def __call__(self, name=None):
if self.user_runbook:
return create_call_rb(self.user_runbook, name=name)
Expand All @@ -64,6 +86,25 @@ def __get__(self, instance, cls):
if cls is None:
return self

if self.imported_action:
# Only endpoints of type existing are supported
sig = inspect.signature(self.user_func)
for name, _ in sig.parameters.items():
if name in ["endpoints", "credentials", "default"]:
if name in ["endpoints", "credentials"]:
LOG.error(
"{} are not supported for imported runbooks. Please use existing {} in the tasks.".format(
name, name
)
)
else:
LOG.error(
"{} are not supported for imported runbooks".format(name)
)
sys.exit(
"Unknown parameter '{}' for imported runbooks".format(name)
)

super(action, self).__get__(instance, cls)

# System action names
Expand All @@ -88,6 +129,33 @@ def __get__(self, instance, cls):
if gui_display_name and gui_display_name.default != action_name:
action_name = gui_display_name.default

# Case for imported runbooks in blueprints
if self.imported_action:

# Mapping is compulsory for profile actions
if self.task_target_mapping:
# For now it is used to map runbook task's target to bp entities for PROFILE
# In runbook, the target will be endpoint. So it will be changed to target_endpoint
for _task in self.user_runbook.tasks[1:]:
if _task.target_any_local_reference:
_task.exec_target_reference = _task.target_any_local_reference
_task.target_any_local_reference = None

if _task.name in self.task_target_mapping:
_task.target_any_local_reference = self.task_target_mapping[
_task.name
]

# Non-Profile actions
else:
for _task in self.user_runbook.tasks[1:]:
if (
_task.target_any_local_reference
and _task.target_any_local_reference.kind == "app_endpoint"
):
_task.exec_target_reference = _task.target_any_local_reference
_task.target_any_local_reference = self.task_target

# Finally create the action
self.user_action = _action_create(
**{
Expand All @@ -104,3 +172,13 @@ def __get__(self, instance, cls):

class parallel:
__calm_type__ = "parallel"


def get_runbook_action(runbook_obj, targets={}):
"""
Get action from the runbook object
"""

user_func = runbook_obj.user_func
action_obj = action(user_func, task_target_mapping=targets, imported_action=True)
return action_obj
52 changes: 29 additions & 23 deletions calm/dsl/builtins/models/ahv_vm_disk.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,31 +44,37 @@ def compile(cls):
image_name = image_ref.get("name")
device_type = cdict["device_properties"].get("device_type")

image_cache_data = Cache.get_entity_data(
entity_type=CACHE.ENTITY.AHV_DISK_IMAGE,
name=image_name,
image_type=IMAGE_TYPE_MAP[device_type],
account_uuid=account_uuid,
)
if not image_cache_data:
LOG.debug(
"Ahv Disk Image (name = '{}') not found in registered nutanix_pc account (uuid = '{}') in project (name = '{}')".format(
image_name, account_uuid, project["name"]
)
if image_name.startswith("@@{") and image_name.endswith("}@@"):
cdict["data_source_reference"] = {
"kind": "image",
"uuid": image_name,
}
else:
image_cache_data = Cache.get_entity_data(
entity_type=CACHE.ENTITY.AHV_DISK_IMAGE,
name=image_name,
image_type=IMAGE_TYPE_MAP[device_type],
account_uuid=account_uuid,
)
LOG.error(
"Ahv Disk Image {} of type {} not found. Please run: calm update cache".format(
image_name, IMAGE_TYPE_MAP[device_type]
if not image_cache_data:
LOG.debug(
"Ahv Disk Image (name = '{}') not found in registered nutanix_pc account (uuid = '{}') in project (name = '{}')".format(
image_name, account_uuid, project["name"]
)
)
)
sys.exit(-1)

image_uuid = image_cache_data.get("uuid", "")
cdict["data_source_reference"] = {
"kind": "image",
"name": image_name,
"uuid": image_uuid,
}
LOG.error(
"Ahv Disk Image {} of type {} not found. Please run: calm update cache".format(
image_name, IMAGE_TYPE_MAP[device_type]
)
)
sys.exit(-1)

image_uuid = image_cache_data.get("uuid", "")
cdict["data_source_reference"] = {
"kind": "image",
"name": image_name,
"uuid": image_uuid,
}

return cdict

Expand Down
2 changes: 2 additions & 0 deletions calm/dsl/builtins/models/entity.py
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,8 @@ def _validate_attr(cls, vdict, name, value):

elif isinstance(type(value), DescriptorType):
ValidatorType = None
# Set action_name attribute in action object
setattr(value, "action_name", name)
is_array = False

if ValidatorType is not None:
Expand Down
10 changes: 5 additions & 5 deletions calm/dsl/builtins/models/profile.py
Original file line number Diff line number Diff line change
Expand Up @@ -114,18 +114,18 @@ def get_config_action_name(config, config_type):
r"[^A-Za-z0-9-_]+", "_", suffix
)

if cdict["restore_config_list"] and not cdict["snapshot_config_list"]:
if cdict.get("restore_config_list") and not cdict.get("snapshot_config_list"):
LOG.error(
"No RestoreConfig found. Please add/associate a RestoreConfig with the SnapshotConfig(s)."
)
sys.exit("Missing snapshot configs")

if cdict["snapshot_config_list"] and not cdict["restore_config_list"]:
if cdict.get("snapshot_config_list") and not cdict.get("restore_config_list"):
LOG.error(
"No snapshot config found. Cannot use RestoreConfig without a SnapshotConfig."
)
sys.exit("Missing restore configs")
for config in cdict["patch_list"]:
for config in cdict.get("patch_list", []):
if not isinstance(config, PatchConfigSpecType):
LOG.error(
"{} is not an object of PatchConfig. patch_config is an array of PatchConfig objects".format(
Expand All @@ -135,7 +135,7 @@ def get_config_action_name(config, config_type):
sys.exit("{} is not an instance of PatchConfig".format(config))
config = set_config_type_based_on_target(config, "patch")

for config in cdict["restore_config_list"]:
for config in cdict.get("restore_config_list", []):
if not isinstance(config, RestoreConfigSpecType):
LOG.error(
"{} is not an object of RestoreConfig. restore_configs is an array of AppProtection.RestoreConfig objects".format(
Expand All @@ -149,7 +149,7 @@ def get_config_action_name(config, config_type):
if config_action:
cdict["action_list"].append(config_action)

for config in cdict["snapshot_config_list"]:
for config in cdict.get("snapshot_config_list", []):
if not isinstance(config, SnapshotConfigSpecType):
LOG.error(
"{} is not an object of SnapshotConfig. snapshot_configs is an array of AppProtection.SnapshotConfig objects".format(
Expand Down
1 change: 1 addition & 0 deletions calm/dsl/builtins/models/runbook.py
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ def __init__(self, user_func):
self.user_func = user_func
self.user_runbook = None
self.task_target = None

if self.__class__ == runbook:
self.__get__()

Expand Down
98 changes: 98 additions & 0 deletions examples/AHV_MACRO_BLUEPRINT/blueprint.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
"""
This blueprint uses macro in nic and disk
"""

import os # no_qa

from calm.dsl.builtins import * # no_qa
from examples.AHV_MACRO_BLUEPRINT.sample_runbook import DslSetVariableRunbook


# Secret Variables
CRED_USERNAME = read_local_file("cred_username")
CRED_PASSWORD = read_local_file("cred_password")


class Service1(Service):
"""Sample Service"""

# disk image uuid to be used in disk image
Custom_disk_uuid = CalmVariable.Simple.string(
"e7b96d85-f41b-40a1-bd23-310b7de14eb1"
)


class Package1(Package):
"""Sample package"""

services = [ref(Service1)]


class VmResources(AhvVmResources):

memory = 2
vCPUs = 2
cores_per_vCPU = 2
disks = [
AhvVmDisk.Disk.Scsi.cloneFromImageService(
"@@{Service1.Custom_disk_uuid}@@", bootable=True
)
]
nics = [AhvVmNic.NormalNic.ingress("@@{custom_nic.uuid}@@")]


class VMSubstrate(Substrate):
"""Sample Substrate"""

provider_spec = ahv_vm(
name="vm-@@{calm_array_index}@@-@@{calm_time}@@", resources=VmResources
)

@action
def __pre_create__():
CalmTask.SetVariable.escript(
name="Pre_create task1",
filename=os.path.join("scripts", "pre_create_script.py"),
target=ref(VMSubstrate),
variables=["custom_nic"],
)


class MacroBlueprintDeployment(Deployment):
"""Sample Deployment"""

packages = [ref(Package1)]
substrate = ref(VMSubstrate)


class MacroBlueprintProfile(Profile):
"""Sample Profile"""

foo1 = CalmVariable.Simple("bar1", runtime=True)
foo2 = CalmVariable.Simple("bar2", runtime=True)
deployments = [MacroBlueprintDeployment]

# For profile, actions task_target_mapping is compulsory
http_task_action = get_runbook_action(
DslSetVariableRunbook,
targets={
"Task1": ref(Service1),
"Task2": ref(Service1),
"Task3": ref(Service1),
}
)

@action
def test_profile_action():
"""Sample description for a profile action"""
CalmTask.Exec.ssh(name="Task5", script='echo "Hello"', target=ref(Service1))


class MacroBlueprint(Blueprint):
"""Blueprint using macro in disk and nics"""

services = [Service1]
packages = [Package1]
substrates = [VMSubstrate]
profiles = [MacroBlueprintProfile]
credentials = [basic_cred(CRED_USERNAME, CRED_PASSWORD, default=True)]
Loading

0 comments on commit 67c0d97

Please sign in to comment.