Skip to content
This repository has been archived by the owner on Apr 10, 2024. It is now read-only.

Commit

Permalink
Merge pull request #11 from gtema/compute
Browse files Browse the repository at this point in the history
feat(compute): address hypervisor and aggregate resources
  • Loading branch information
gtema authored Jan 26, 2024
2 parents 9d2c299 + b688280 commit 0b4fb30
Show file tree
Hide file tree
Showing 12 changed files with 368 additions and 1,123 deletions.
5 changes: 4 additions & 1 deletion .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,10 @@ repos:
rev: v1.4.1
hooks:
- id: mypy
files: '^codegenerator/.*\.py$'
language: python
types: [python]
args: ["codegenerator"]
pass_filenames: false
additional_dependencies:
- types-decorator
- types-PyYAML
14 changes: 9 additions & 5 deletions codegenerator/common/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,7 @@ def find_resource_schema(
if (
parent
and resource_name
and (parent == get_plural_form(resource_name))
and parent == get_plural_form(resource_name)
):
return (schema["items"], parent)
elif (
Expand Down Expand Up @@ -184,9 +184,9 @@ def get_resource_names_from_url(path: str):
if "{" not in path_element:
el = path_element.replace("-", "_")
if el[-3:] == "ies":
path_resource_names.append(el[0:-3] + "y")
part = el[0:-3] + "y"
elif el[-4:] == "sses":
path_resource_names.append(el[0:-2])
part = el[0:-2]
elif (
el[-1] == "s"
and el[-3:] != "dns"
Expand All @@ -195,9 +195,13 @@ def get_resource_names_from_url(path: str):
# quota/details
and el != "details"
):
path_resource_names.append(el[0:-1])
part = el[0:-1]
else:
path_resource_names.append(el)
part = el
if part.startswith("os_"):
# We should remove `os_` prefix from resource name
part = part[3:]
path_resource_names.append(part)
if len(path_resource_names) > 1 and (
path_resource_names[-1]
in [
Expand Down
166 changes: 106 additions & 60 deletions codegenerator/metadata.py
Original file line number Diff line number Diff line change
Expand Up @@ -60,9 +60,36 @@ def generate(
[x for x in common.get_resource_names_from_url(path)]
)
if args.service_type == "compute" and resource_name in [
"os_volumes_boot"
"agent",
"baremetal_node",
"cell",
"cell/capacity",
"cell/info",
"cell/sync_instance",
"certificate",
"cloudpipe",
"fping",
"fixed_ip",
"floating_ip_dns",
"floating_ip_dns/entry",
"floating_ip_pool",
"floating_ip_bulk",
"host",
"host/reboot",
"host/shutdown",
"host/startup",
"image",
"image/metadata",
"network",
"security_group_default_rule",
"security_group_rule",
"security_group",
"snapshot",
"tenant_network",
"volume",
"volumes_boot",
]:
# We do not need to produce anything for old os_volumes_boot
# We do not need to produce anything for deprecated APIs
continue
resource_model = metadata.resources.setdefault(
f"{args.service_type}.{resource_name}",
Expand Down Expand Up @@ -136,10 +163,16 @@ def generate(
operation_key = "update"
elif (
args.service_type == "compute"
and resource_name == "flavor/os_flavor_access"
and resource_name == "flavor/flavor_access"
and method == "get"
):
operation_key = "list"
elif (
args.service_type == "compute"
and resource_name == "aggregate/image"
and method == "post"
):
operation_key = "action"

elif response_schema and (
method == "get"
Expand Down Expand Up @@ -239,15 +272,18 @@ def generate(
body_schema = operation.requestBody["content"][
"application/json"
]["schema"]
bodies = body_schema["oneOf"]
discriminator = body_schema.get(
"x-openstack", {}
).get("discriminator")
if discriminator != "action":
raise RuntimeError(
"Cannot generate metadata for %s since requet body is not having action discriminator"
% path
)
bodies = body_schema.get(
"oneOf", [body_schema]
)
if len(bodies) > 1:
discriminator = body_schema.get(
"x-openstack", {}
).get("discriminator")
if discriminator != "action":
raise RuntimeError(
"Cannot generate metadata for %s since requet body is not having action discriminator"
% path
)
for body in bodies:
action_name = body.get(
"x-openstack", {}
Expand Down Expand Up @@ -309,6 +345,14 @@ def generate(
op_model.targets[
"rust-cli"
] = rust_cli_params

op_model = post_process_operation(
args.service_type,
resource_name,
operation_name,
op_model,
)

resource_model.operations[
operation_name
] = op_model
Expand All @@ -332,60 +376,20 @@ def generate(
operation_key
)

if args.service_type == "compute":
# Keypairs have non standard response key.
# Help codegenerator by setting them into
# metadata.
if resource_name == "os_keypair":
if operation_type in [
"create",
"show",
]:
rust_sdk_params.response_key = (
"keypair"
)
rust_cli_params.response_key = (
"keypair"
)
elif operation_type == "list":
rust_sdk_params.response_key = (
"keypairs"
)
rust_cli_params.response_key = (
"keypair"
)
rust_sdk_params.response_list_item_key = (
"keypair"
)
elif (
resource_name == "flavor/os_flavor_access"
):
if operation_type == "list":
rust_sdk_params.response_key = (
"flavor_access"
)
rust_cli_params.response_key = (
"flavor_access"
)
elif resource_name == "flavor/os_extra_spec":
if operation_type == "create":
rust_sdk_params.response_key = (
"extra_specs"
)
rust_cli_params.response_key = (
"extra_specs"
)
elif args.service_type == "image":
# Image schemas are a JSON operation
if resource_name.startswith("schema"):
rust_cli_params.operation_type = "json"

op_model.targets["rust-sdk"] = rust_sdk_params
if rust_cli_params and not (
args.service_type == "identity"
and operation_key == "check"
):
op_model.targets["rust-cli"] = rust_cli_params

op_model = post_process_operation(
args.service_type,
resource_name,
operation_key,
op_model,
)

resource_model.operations[operation_key] = op_model
pass
for res_name, res_data in metadata.resources.items():
Expand Down Expand Up @@ -589,3 +593,45 @@ def get_module_name(name):
elif name in ["default"]:
return "default"
return "_".join(x.lower() for x in re.split(common.SPLIT_NAME_RE, name))


def post_process_operation(
service_type: str, resource_name: str, operation_name: str, operation
):
if service_type == "compute":
operation = post_process_compute_operation(
resource_name, operation_name, operation
)
elif service_type == "image":
operation = post_process_image_operation(
resource_name, operation_name, operation
)
return operation


def post_process_compute_operation(
resource_name: str, operation_name: str, operation
):
if resource_name == "aggregate":
if operation_name in ["set-metadata", "add-host", "remove-host"]:
operation.targets["rust-sdk"].response_key = "aggregate"
operation.targets["rust-cli"].response_key = "aggregate"
elif resource_name == "availability_zone":
if operation_name in ["get", "list_detailed"]:
operation.targets["rust-sdk"].response_key = "availabilityZoneInfo"
operation.targets["rust-cli"].response_key = "availabilityZoneInfo"
elif resource_name == "keypair":
if operation_name == "list":
operation.targets["rust-sdk"].response_list_item_key = "keypair"

return operation


def post_process_image_operation(
resource_name: str, operation_name: str, operation
):
if resource_name.startswith("schema"):
# Image schemas are a JSON operation
operation.targets["rust-cli"].operation_type = "json"

return operation
2 changes: 2 additions & 0 deletions codegenerator/openapi/nova.py
Original file line number Diff line number Diff line change
Expand Up @@ -421,6 +421,8 @@ def _get_schema_ref(
name, TypeSchema(**nova_schemas.AGGREGATE_CONTAINER_SCHEMA)
)
ref = f"#/components/schemas/{name}"
elif name == "Os_AggregatesImagesResponse":
return (None, None)
# /os-assisted-volume-snapshots
elif name == "Os_Assisted_Volume_SnapshotsCreateResponse":
schema = openapi_spec.components.schemas.setdefault(
Expand Down
24 changes: 14 additions & 10 deletions codegenerator/openapi/nova_schemas.py
Original file line number Diff line number Diff line change
Expand Up @@ -285,26 +285,30 @@
"format": "date-time",
"description": "The date and time when the resource was created.",
},
"deleted": {
"type": "boolean",
"description": "A boolean indicates whether this aggregate is deleted or not, if it has not been deleted, false will appear.",
},
"deleted_at": {
"type": ["string", "null"],
"format": "date-time",
"description": "The date and time when the resource was deleted. If the resource has not been deleted yet, this field will be null.",
},
"updated_at": {
"type": ["string", "null"],
"format": "date-time",
"description": "The date and time when the resource was updated, if the resource has not been updated, this field will show as null.",
},
"deleted": {
"type": "boolean",
"description": "A boolean indicates whether this aggregate is deleted or not, if it has not been deleted, false will appear.",
"id": {
"type": "integer",
"description": "The ID of the host aggregate.",
},
"metadata": parameter_types.metadata,
"hosts": {
"type": "array",
"description": "A list of host ids in this aggregate.",
"items": {"type": "string"},
},
"metadata": parameter_types.metadata_with_null,
"updated_at": {
"type": ["string", "null"],
"format": "date-time",
"description": "The date and time when the resource was updated, if the resource has not been updated, this field will show as null.",
},
"uuid": {
"type": "string",
"format": "uuid",
Expand Down Expand Up @@ -490,7 +494,7 @@
"description": "The hypervisor host name provided by the Nova virt driver. For the Ironic driver, it is the Ironic node uuid.",
},
"id": {
"type": ["integer", "string"],
"type": "string",
"description": "The id of the hypervisor. From version 2.53 it is a string as UUID",
},
"state": {
Expand Down
34 changes: 30 additions & 4 deletions codegenerator/rust_cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -270,20 +270,33 @@ def type_hint(self):

@property
def imports(self):
imports = set(["crate::common::parse_key_val"])
imports = set([])
if not isinstance(self.value_type, common_rust.Option):
imports.add("crate::common::parse_key_val")
else:
imports.add("crate::common::parse_key_val_opt")
imports.update(self.value_type.imports)
return imports

@property
def clap_macros(self):
return set(
macros = set(
[
"long",
'value_name="key=value"',
f"value_parser=parse_key_val::<String, {self.value_type.type_hint}>",
]
)

if not isinstance(self.value_type, common_rust.Option):
macros.add(
f"value_parser=parse_key_val::<String, {self.value_type.type_hint}>",
)
else:
macros.add(
f"value_parser=parse_key_val_opt::<String, {self.value_type.item_type.type_hint}>",
)
return macros


class StringEnum(common_rust.StringEnum):
imports: set[str] = set(["clap::ValueEnum"])
Expand Down Expand Up @@ -458,6 +471,7 @@ def convert_model(
typ: BasePrimitiveType | BaseCombinedType | BaseCompoundType | None = (
None
)

if isinstance(type_model, model.Reference):
model_ref = type_model
type_model = self._get_adt_by_reference(model_ref)
Expand Down Expand Up @@ -949,7 +963,19 @@ def generate(
).get("action-name")
== args.operation_name
):
response_def = candidate
if resource_name in candidate.get(
"properties", {}
):
# If there is a object with
# resource_name in the props -
# this must be what we want to
# look at
response_def = candidate[
"properties"
][resource_name]
else:
response_def = candidate
break
elif (
resource_name
and candidate.get("type") == "object"
Expand Down
Loading

0 comments on commit 0b4fb30

Please sign in to comment.