from typing import Any
from idem_vra.helpers.mapper import add_properties
from idem_vra.helpers.mapper import omit_properties
from idem_vra.helpers.models import StateReturn
__contracts__ = ["resource"]
TREQ = {"present": {"require": ["vra.iaas.project.present"]}, "absent": {"require": []}}
async def present(hub, ctx, name: str, actionType: Any, orgId: Any, **kwargs):
"""
:param string actionType: (required in body) Type of the action
:param string name: (required in body) Name of the action
:param string orgId: (required in body) Organization ID of the action.
:param boolean asyncDeployed: (optional in body)
:param string compressedContent: (optional in body) base64encoded ZIP of action content (source & dependencies)
:param object configuration: (optional in body) Configuration of the action for specific providers
:param string contentId: (optional in body) ID of the actions saved compressed content
:param string dependencies: (optional in body) A list of libraries to import, delimited by a comma
:param string description: (optional in body) Description of the action
:param string entrypoint: (optional in body) Name of the entry function of the action
:param string id: (optional in body) ID of the action
:param object inputs: (optional in body) Map defining the inputs of the action
:param integer memoryInMB: (optional in body) Runtime RAM constraints in megabytes
:param object metadata: (optional in body) Additional properties used by ABX
:param integer prePolyglotMemoryLimitInMB: (optional in body)
:param string projectId: (optional in body) Project Id of the action (required for non-system actions)
:param string provider: (optional in body) Provider used for code execution
:param string runtime: (optional in body) Runtime of the action (python, nodejs, etc...)
:param boolean scalable: (optional in body)
:param string selfLink: (optional in body)
:param boolean shared: (optional in body) Flag indicating if the action can be shared across projects.
:param boolean showMemoryAlert: (optional in body)
:param string source: (optional in body) Source of the action as string
:param boolean system: (optional in body) Flag indicating if the action is a system action.
:param integer timeoutSeconds: (optional in body) Defines how long an action can run (default 600)
"""
try:
state = ActionsStateImpl(hub, ctx)
return await state.present(hub, ctx, name, actionType, orgId, **kwargs)
except Exception as error:
hub.log.error("Error during enforcing present state: actions")
hub.log.error(str(error))
raise error
[docs]async def absent(hub, ctx, name: str, **kwargs):
"""
:param string p_id: (required in path) ID of the action
:param string q_projectId: (required in query) Project ID of action (required for non-system actions)
:param boolean force: (optional in query) force
"""
"""
:param string name: (required) name of the resource
"""
try:
state = ActionsStateImpl(hub, ctx)
return await state.absent(hub, ctx, name, **kwargs)
except Exception as error:
hub.log.error("Error during enforcing absent state: actions")
hub.log.error(str(error))
raise error
[docs]async def describe(hub, ctx):
try:
state = ActionsStateImpl(hub, ctx)
return await state.describe(hub, ctx)
except Exception as error:
hub.log.error("Error during describe: actions")
hub.log.error(str(error))
raise error
[docs]def is_pending(hub, ret: dict, state: str = None, **pending_kwargs):
try:
state = ActionsStateImpl(hub, None)
return state.is_pending(hub, ret, state, **pending_kwargs)
except Exception as error:
hub.log.error("Error during is_pending: actions")
hub.log.error(str(error))
raise error
[docs]class ActionsState:
def __init__(self, hub, ctx):
self.hub = hub
self.ctx = ctx
[docs] async def present(self, hub, ctx, name: str, actionType: Any, orgId: Any, **kwargs):
search_result = (await self.paginate_find(hub, ctx))["ret"]
for s in search_result.content:
if name == s["name"] and True:
hub.log.info(
f'Returning resource actions "{s["name"]}" due to existing resource "{name}"'
)
s = await self.remap_resource_structure(hub, ctx, s)
return StateReturn(
result=True,
comment=f"Resource actions {name} already exists.",
old=s,
new=s,
)
res = (
await hub.exec.vra.abx.actions.create_using_post(
ctx, actionType, name, orgId, **kwargs
)
)["ret"]
res = await self.remap_resource_structure(hub, ctx, res)
return StateReturn(
result=True,
comment=f"Creation of actions {name} success.",
old=None,
new=res,
)
[docs] async def absent(self, hub, ctx, name: str, **kwargs):
search_result = (await self.paginate_find(hub, ctx))["ret"]
resource = None
for s in search_result.content:
if name == s["name"] and True:
hub.log.info(
f'Found resource actions "{s["name"]}" due to existing resource "{name}"'
)
s = await self.remap_resource_structure(hub, ctx, s)
resource = s
if resource:
# it exists!
delete_kwargs = {}
delete_kwargs["p_id"] = resource.get("id")
delete_kwargs["q_projectId"] = resource.get("q_projectId")
hub.log.debug(f"actions with name = {resource.get('name')} already exists")
await hub.exec.vra.abx.actions.delete_using_delete1(ctx, **delete_kwargs)
return StateReturn(
result=True,
comment=f"Resource with name = {resource.get('name')} deleted.",
old=resource,
new=None,
)
return StateReturn(
result=True,
comment=f"Resource with name = {name} is already absent.",
old=None,
new=None,
)
[docs] async def describe(self, hub, ctx):
result = {}
res = await self.paginate_find(hub, ctx)
for obj in res.get("ret", {}).get("content", []):
# Keep track of name and id properties as they may get remapped
obj_name = obj.get("name", "unknown")
obj_id = obj.get("id", "unknown")
obj = await self.remap_resource_structure(hub, ctx, obj)
# Define props
props = [{key: value} for key, value in obj.items()]
# Build result
result[f"{obj_name}-{obj_id.split('-')[-1]}"] = {
"vra.abx.actions.present": props
}
return result
[docs] async def paginate_find(self, hub, ctx, **kwargs):
"""
Paginate through all resources using their 'find' method.
"""
res = await hub.exec.vra.abx.actions.get_all_using_get(ctx, **kwargs)
numberOfElements = res.get("ret", {}).get("numberOfElements", 0)
totalElements = res.get("ret", {}).get("totalElements", 0)
initialElements = numberOfElements
if numberOfElements != totalElements and totalElements != 0:
while initialElements < totalElements:
hub.log.debug(
f"Requesting actions with offset={initialElements} out of {totalElements}"
)
pres = await hub.exec.vra.abx.actions.get_all_using_get(
ctx, skip=initialElements
)
initialElements += pres.get("ret", {}).get("numberOfElements", 0)
aggO = res.get("ret", {}).get("content", [])
aggN = pres.get("ret", {}).get("content", [])
res["ret"]["content"] = [*aggO, *aggN]
res["ret"]["numberOfElements"] = initialElements
return res
[docs] def is_pending(self, hub, ret: dict, state: str = None, **pending_kwargs):
"""
State reconciliation
"""
hub.log.debug(f'Running is_pending for resource: {ret.get("__id__", None)}...')
is_pending_result = False
hub.log.debug(
f'is_pending_result for resource "{ret.get("__id__", None)}": {is_pending_result}'
)
return is_pending_result
[docs] async def remap_resource_structure(self, hub, ctx, obj: dict) -> dict:
schema_mapper = {"add": [], "omit": ["selfLink", "contentId"]}
# Perform resource mapping by adding properties and omitting properties.
# Property renaming is addition followed by omission.
if schema_mapper:
resource_name = "actions"
hub.log.debug(f"Remapping resource {resource_name}...")
obj = await add_properties(obj, schema_mapper.get("add", []))
obj = omit_properties(obj, schema_mapper.get("omit", []))
return obj
# ====================================
# State override
# ====================================
import gzip
import base64
from idem_vra.helpers.models import StateReturn
# Adding this function to override ,
# because out of box has orgId as mandate , But we need to have name and projectId as mandate
[docs]async def present(hub, ctx, name: str, **kwargs):
try:
state = ActionsStateImpl(hub, ctx)
return await state.present(hub, ctx, name, **kwargs)
except Exception as error:
hub.log.error("Error during enforcing present state: actions")
hub.log.error(str(error))
raise error
[docs]class ActionsStateImpl(ActionsState):
[docs] async def paginate_find(self, hub, ctx, **kwargs):
"""
Paginate through all resources using their 'find' method.
"""
res = await hub.exec.vra.abx.actions.get_all_using_get(ctx, **kwargs)
total_pages = res["ret"]["totalPages"]
page_list = list(range(1, total_pages + 1))
for i in page_list:
new_data = await hub.exec.vra.abx.actions.get_all_using_get(ctx, page=i)
for j in new_data["ret"]["content"]:
res["ret"]["content"].append(j)
return res
[docs] async def present(self, hub, ctx, name: str, **kwargs):
# Search function to check all inventory
search_result = (await self.paginate_find(hub, ctx))["ret"]
for s in search_result.content:
if name == s["name"] and True:
hub.log.info(
f'Returning resource actions "{s["name"]}" due to existing resource "{name}"'
)
s = await self.remap_resource_structure(hub, ctx, s)
return StateReturn(
result=True,
comment=f"Resource actions {name} already exists.",
old=s,
new=s,
)
payload = {"name": name}
# adding all extra parameters to payload
for key, value in kwargs.items():
payload[key] = value
# checking if Bundle is part of sls payload or not
if "bundle" in payload.keys():
for bundle in payload["bundle"]:
byte_encoded_data = hub.tool.binary_data.read_binary(bundle)
compressed_data = gzip.compress(byte_encoded_data)
base64_data = base64.b64encode(compressed_data).decode("utf-8")
payload["compressedContent"] = base64_data
res = await hub.exec.vra.rest.request(
ctx, method="post", path="/abx/api/resources/actions", json=payload
)
res = await self.remap_resource_structure(hub, ctx, res)
return StateReturn(
result=True,
comment=f"Creation of actions {name} success.",
old=None,
new=res,
)
[docs] async def absent(self, hub, ctx, name: str, **kwargs):
search_result = (await self.paginate_find(hub, ctx))["ret"]
resource = None
for s in search_result.content:
if name == s["name"] and True:
hub.log.info(
f'Found resource actions "{s["name"]}" due to existing resource "{name}"'
)
s = await self.remap_resource_structure(hub, ctx, s)
resource = s
if resource:
# it exists!
hub.log.debug(f"actions with name = {resource.get('name')} already exists")
await hub.exec.vra.abx.actions.delete_using_delete1(
ctx,
resource["id"],
resource["projectId"],
)
return StateReturn(
result=True,
comment=f"Resource with name = {resource.get('name')} deleted.",
old=resource,
new=None,
)
return StateReturn(
result=True,
comment=f"Resource with name = {name} is already absent.",
old=None,
new=None,
)
[docs] async def describe(self, hub, ctx):
result = {}
data = await self.paginate_find(hub, ctx)
# Removing System ABX action from main payload
res = [d for d in data["ret"]["content"] if not d["system"]]
for i in res:
if i["contentId"] is not None:
data = {
"actions": [
{
"id": i["id"],
"orgId": i["orgId"],
"projectId": i["projectId"],
}
]
}
export_action = await hub.exec.vra.rest.request(
ctx,
method="post",
path="/abx/api/resources/actions/export",
json=data,
)
# Added dummy uri for this used case uri = 'file://'
extracted_files = hub.tool.binary_data.write_binary(
uri="zip://",
byte_data=export_action["ret"],
extract_all_with_extension=".zip",
)
i["bundle"] = extracted_files
for obj in res:
# Keep track of name and id properties as they may get remapped
obj_name = obj.get("name", "unknown")
obj_id = obj.get("id", "unknown")
obj = await self.remap_resource_structure(hub, ctx, obj)
# Define props
props = [{key: value} for key, value in obj.items()]
# Build result
result[f"{obj_name}-{obj_id.split('-')[-1]}"] = {
"vra.abx.actions.present": props
}
return result