File: //snap/google-cloud-cli/current/lib/googlecloudsdk/api_lib/compute/transforms.py
# -*- coding: utf-8 -*- #
# Copyright 2015 Google LLC. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""Compute resource transforms and symbols dict.
A resource transform function converts a JSON-serializable resource to a string
value. This module contains built-in transform functions that may be used in
resource projection and filter expressions.
NOTICE: Each TransformFoo() method is the implementation of a foo() transform
function. Even though the implementation here is in Python the usage in resource
projection and filter expressions is language agnostic. This affects the
Pythonicness of the Transform*() methods:
(1) The docstrings are used to generate external user documentation.
(2) The method prototypes are included in the documentation. In particular the
prototype formal parameter names are stylized for the documentation.
(3) The 'r', 'kwargs', and 'projection' args are not included in the external
documentation. Docstring descriptions, other than the Args: line for the
arg itself, should not mention these args. Assume the reader knows the
specific item the transform is being applied to. When in doubt refer to
the output of $ gcloud topic projections.
(4) The types of some args, like r, are not fixed until runtime. Other args
may have either a base type value or string representation of that type.
It is up to the transform implementation to silently do the string=>type
conversions. That's why you may see e.g. int(arg) in some of the methods.
(5) Unless it is documented to do so, a transform function must not raise any
exceptions related to the resource r. The `undefined' arg is used to
handle all unusual conditions, including ones that would raise exceptions.
Exceptions for arguments explicitly under the caller's control are OK.
"""
from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals
from googlecloudsdk.api_lib.compute import constants
from googlecloudsdk.api_lib.compute import instance_utils
from googlecloudsdk.api_lib.compute import path_simplifier
from googlecloudsdk.core.resource import resource_transform
import six
def TransformFirewallRule(r, undefined=''):
"""Returns a compact string describing a firewall rule.
The compact string is a comma-separated list of PROTOCOL:PORT_RANGE items.
If a particular protocol has no port ranges then only the protocol is listed.
Args:
r: JSON-serializable object.
undefined: Returns this value if the resource cannot be formatted.
Returns:
A compact string describing the firewall rule in r.
"""
protocol = resource_transform.GetKeyValue(r, 'IPProtocol', None)
if protocol is None:
return undefined
rule = []
port_ranges = resource_transform.GetKeyValue(r, 'ports', None)
try:
for port_range in port_ranges:
rule.append('{0}:{1}'.format(protocol, port_range))
except TypeError:
rule.append(protocol)
return ','.join(rule)
def TransformOrganizationFirewallRule(rule, undefined=''):
"""Returns a compact string describing an organization firewall rule.
The compact string is a comma-separated list of PROTOCOL:PORT_RANGE items.
If a particular protocol has no port ranges then only the protocol is listed.
Args:
rule: JSON-serializable object.
undefined: Returns this value if the resource cannot be formatted.
Returns:
A compact string describing the organizatin firewall rule in the rule.
"""
protocol = resource_transform.GetKeyValue(rule, 'ipProtocol', None)
if protocol is None:
return undefined
result = []
port_ranges = resource_transform.GetKeyValue(rule, 'ports', None)
try:
for port_range in port_ranges:
result.append('{0}:{1}'.format(protocol, port_range))
except TypeError:
result.append(protocol)
return ','.join(result)
def TransformImageAlias(r, undefined=''):
"""Returns a comma-separated list of alias names for an image.
Args:
r: JSON-serializable object.
undefined: Returns this value if the resource cannot be formatted.
Returns:
A comma-separated list of alias names for the image in r.
"""
name = resource_transform.GetKeyValue(r, 'name', None)
if name is None:
return undefined
project = resource_transform.TransformScope(
resource_transform.GetKeyValue(r, 'selfLink', ''),
'projects').split('/')[0]
aliases = [alias for alias, value in constants.IMAGE_ALIASES.items()
if name.startswith(value.name_prefix)
and value.project == project]
return ','.join(aliases)
def TransformLocation(r, undefined=''):
"""Return the region or zone name.
Args:
r: JSON-serializable object.
undefined: Returns this value if the resource cannot be formatted.
Returns:
The region or zone name.
"""
for scope in ('zone', 'region'):
location = resource_transform.GetKeyValue(r, scope, None)
if location:
return resource_transform.TransformBaseName(location, undefined)
return undefined
def TransformLocationScope(r, undefined=''):
"""Return the location scope name, either region or zone.
Args:
r: JSON-serializable object.
undefined: Returns this value if the resource cannot be formatted.
Returns:
The location scope name, either region or zone.
"""
for scope in ('zone', 'region'):
location = resource_transform.GetKeyValue(r, scope, None)
if location:
return scope
return undefined
def TransformMachineType(r, undefined=''):
"""Return the formatted name for a machine type.
Args:
r: JSON-serializable object.
undefined: Returns this value if the resource cannot be formatted.
Returns:
The formatted name for a machine type.
"""
if not isinstance(r, six.string_types):
return undefined
custom_family, custom_cpu, custom_ram = \
instance_utils.GetCpuRamVmFamilyFromCustomName(r)
if not custom_family or not custom_cpu or not custom_ram:
return r
# Restricting output to 2 decimal places
custom_ram_gb = '{0:.2f}'.format(float(custom_ram) / (2**10))
return 'custom ({0}, {1} vCPU, {2} GiB)'.format(custom_family, custom_cpu,
custom_ram_gb)
def TransformNextMaintenance(r, undefined=''):
"""Returns the timestamps of the next scheduled maintenance.
All timestamps are assumed to be ISO strings in the same timezone.
Args:
r: JSON-serializable object.
undefined: Returns this value if the resource cannot be formatted.
Returns:
The timestamps of the next scheduled maintenance or undefined.
"""
if not r:
return undefined
next_event = min(r, key=lambda x: x.get('beginTime', None))
if next_event is None:
return undefined
begin_time = next_event.get('beginTime', None)
if begin_time is None:
return undefined
end_time = next_event.get('endTime', None)
if end_time is None:
return undefined
return '{0}--{1}'.format(begin_time, end_time)
def TransformOperationHttpStatus(r, undefined=''):
"""Returns the HTTP response code of an operation.
Args:
r: JSON-serializable object.
undefined: Returns this value if there is no response code.
Returns:
The HTTP response code of the operation in r.
"""
if resource_transform.GetKeyValue(r, 'status', None) == 'DONE':
return (resource_transform.GetKeyValue(r, 'httpErrorStatusCode', None) or
200) # httplib.OK
return undefined
def TransformProject(r, undefined=''):
"""Returns a project name from a selfLink.
Args:
r: JSON-serializable object.
undefined: Returns this value if the resource cannot be formatted.
Returns:
A project name for selfLink from r.
"""
project = resource_transform.TransformScope(
resource_transform.GetKeyValue(r, 'selfLink', ''),
'projects').split('/')[0]
return project or undefined
def TransformName(r, undefined=''):
"""Returns a resource name from an URI.
Args:
r: JSON-serializable object.
undefined: Returns this value if the resource cannot be formatted.
Returns:
A project name for selfLink from r.
"""
if r:
try:
return r.split('/')[-1]
except AttributeError:
pass
return undefined
def TransformQuota(r, undefined=''):
"""Formats a quota as usage/limit.
Args:
r: JSON-serializable object.
undefined: Returns this value if the resource cannot be formatted.
Returns:
The quota in r as usage/limit.
"""
usage = resource_transform.GetKeyValue(r, 'usage', None)
if usage is None:
return undefined
limit = resource_transform.GetKeyValue(r, 'limit', None)
if limit is None:
return undefined
try:
if usage == int(usage) and limit == int(limit):
return '{0}/{1}'.format(int(usage), int(limit))
return '{0:.2f}/{1:.2f}'.format(usage, limit)
except (TypeError, ValueError):
pass
return undefined
def TransformScopedSuffixes(uris, undefined=''):
"""Get just the scoped part of the object the uri refers to."""
if uris:
try:
return sorted([path_simplifier.ScopedSuffix(uri) for uri in uris])
except TypeError:
pass
return undefined
def TransformStatus(r, undefined=''):
"""Returns the machine status with deprecation information if applicable.
Args:
r: JSON-serializable object.
undefined: Returns this value if the resource cannot be formatted.
Returns:
The machine status in r with deprecation information if applicable.
"""
status = resource_transform.GetKeyValue(r, 'status', None)
deprecated = resource_transform.GetKeyValue(r, 'deprecated', '')
if deprecated:
return '{0} ({1})'.format(status, deprecated.get('state', ''))
return status or undefined
def TransformVpnTunnelGateway(vpn_tunnel, undefined=''):
"""Returns the gateway for the specified VPN tunnel resource if applicable.
Args:
vpn_tunnel: JSON-serializable object of a VPN tunnel.
undefined: Returns this value if the resource cannot be formatted.
Returns:
The VPN gateway information in the VPN tunnel object.
"""
target_vpn_gateway = resource_transform.GetKeyValue(vpn_tunnel,
'targetVpnGateway', None)
if target_vpn_gateway is not None:
return target_vpn_gateway
vpn_gateway = resource_transform.GetKeyValue(vpn_tunnel, 'vpnGateway', None)
if vpn_gateway is not None:
return vpn_gateway
return undefined
def TransformZone(r, undefined=''):
"""Returns a zone name from a selfLink.
Args:
r: JSON-serializable object.
undefined: Returns this value if the resource cannot be formatted.
Returns:
A zone name for selfLink from r.
"""
project = resource_transform.TransformScope(
resource_transform.GetKeyValue(r, 'selfLink', ''), 'zones').split('/')[0]
return project or undefined
def TransformTypeSuffix(uri, undefined=''):
"""Get the type and the name of the object the uri refers to."""
try:
# Since the path is assumed valid, we can just take the last two pieces.
return '/'.join(uri.split('/')[-2:]) or undefined
except (AttributeError, IndexError, TypeError):
pass
return undefined
_TRANSFORMS = {
'firewall_rule': TransformFirewallRule,
'org_firewall_rule': TransformOrganizationFirewallRule,
'image_alias': TransformImageAlias,
'location': TransformLocation,
'location_scope': TransformLocationScope,
'machine_type': TransformMachineType,
'next_maintenance': TransformNextMaintenance,
'name': TransformName,
'operation_http_status': TransformOperationHttpStatus,
'project': TransformProject,
'quota': TransformQuota,
'scoped_suffixes': TransformScopedSuffixes,
'status': TransformStatus,
'type_suffix': TransformTypeSuffix,
'vpn_tunnel_gateway': TransformVpnTunnelGateway,
'zone': TransformZone,
}
def GetTransforms():
"""Returns the compute specific resource transform symbol table."""
return _TRANSFORMS