File: //snap/google-cloud-cli/current/lib/surface/bms/networks/update.py
# -*- coding: utf-8 -*- #
# Copyright 2022 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.
"""Bare Metal Solution network update command."""
from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals
from googlecloudsdk.api_lib.bms.bms_client import BmsClient
from googlecloudsdk.api_lib.bms.bms_client import IpRangeReservation
from googlecloudsdk.api_lib.util import waiter
from googlecloudsdk.calliope import base
from googlecloudsdk.command_lib.bms import exceptions
from googlecloudsdk.command_lib.bms import flags
from googlecloudsdk.command_lib.util.args import labels_util
from googlecloudsdk.core import log
from googlecloudsdk.core import resources
DETAILED_HELP = {
'DESCRIPTION':
"""
Update a Bare Metal Solution network.
This call returns immediately, but the update operation may take
several minutes to complete. To check if the operation is complete,
use the `describe` command for the network.
""",
'EXAMPLES':
"""
To update an network called ``my-network'' in region ``us-central1'' with
a new label ``key1=value1'', run:
$ {command} my-network --region=us-central1 --update-labels=key1=value1
To clear all labels, run:
$ {command} my-network --region=us-central1 --clear-labels
""",
}
@base.ReleaseTracks(base.ReleaseTrack.GA)
class Update(base.UpdateCommand):
"""Update a Bare Metal Solution network."""
@staticmethod
def Args(parser):
"""Register flags for this command."""
flags.AddNetworkArgToParser(parser, positional=True)
labels_util.AddUpdateLabelsFlags(parser)
base.ASYNC_FLAG.AddToParser(parser)
flags.AddNetworkIpReservationToParser(parser, hidden=False)
def Run(self, args):
client = BmsClient()
network = args.CONCEPTS.network.Parse()
labels_update = None
labels_diff = labels_util.Diff.FromUpdateArgs(args)
orig_resource = client.GetNetwork(network)
labels_update = labels_diff.Apply(
client.messages.Network.LabelsValue,
orig_resource.labels).GetOrNone()
ip_reservations = _ApplyIpReservationsUpdates(args, orig_resource)
op_ref = client.UpdateNetwork(
network_resource=network, labels=labels_update,
ip_reservations=ip_reservations)
if op_ref.done:
log.UpdatedResource(network.Name(), kind='network')
return op_ref
if args.async_:
log.status.Print('Update request issued for: [{}]\nCheck operation '
'[{}] for status.'.format(network.Name(), op_ref.name))
return op_ref
op_resource = resources.REGISTRY.ParseRelativeName(
op_ref.name,
collection='baremetalsolution.projects.locations.operations',
api_version='v2')
poller = waiter.CloudOperationPollerNoResources(
client.operation_service)
res = waiter.WaitFor(poller, op_resource,
'Waiting for operation [{}] to complete'.format(
op_ref.name))
log.UpdatedResource(network.Name(), kind='network')
return res
@base.ReleaseTracks(base.ReleaseTrack.ALPHA)
class UpdateAlpha(Update):
"""Update a Bare Metal Solution network."""
@staticmethod
def Args(parser):
# Flags which are only available in ALPHA should be added to parser here.
Update.Args(parser)
def _ApplyIpReservationsUpdates(args, existing_network):
"""Applies the changes in args to the reservations in existing_network.
Returns None if no changes were to be applied.
Args:
args: The arguments passed to the command.
existing_network: The existing network.
Returns:
List of IP range reservations after applying updates or None if there are
no changes.
"""
if _IsSpecified(args, 'clear_ip_range_reservations'):
return []
existing_reservations = [
IpRangeReservation(res.startAddress, res.endAddress, res.note)
for res in existing_network.reservations
]
if _IsSpecified(args, 'add_ip_range_reservation'):
res_dict = args.add_ip_range_reservation
_ValidateAgainstSpec(res_dict, flags.IP_RESERVATION_SPEC,
'add-ip-range-reservation')
return existing_reservations + [
IpRangeReservation(res_dict['start-address'], res_dict['end-address'],
res_dict['note'])
]
if _IsSpecified(args, 'remove_ip_range_reservation'):
return _RemoveReservation(existing_reservations,
args.remove_ip_range_reservation)
def _RemoveReservation(reservations, remove_key_dict):
_ValidateAgainstSpec(remove_key_dict, flags.IP_RESERVATION_KEY_SPEC,
'remove-ip-range-reservation')
start_address = remove_key_dict['start-address']
end_address = remove_key_dict['end-address']
for i, res in enumerate(reservations):
if res.start_address == start_address and res.end_address == end_address:
return reservations[:i] + reservations[i + 1:]
raise LookupError('Cannot find an IP range reservation with start-address'
' [{}] and end-address [{}]'.format(start_address,
end_address))
def _ValidateAgainstSpec(dict_to_validate, spec, flag_name):
for prop in spec.keys():
if prop not in dict_to_validate:
raise exceptions.MissingPropertyError(flag_name, prop)
def _IsSpecified(args, name):
"""Returns true if an arg is defined and specified, false otherwise."""
return args.IsKnownAndSpecified(name)
Update.detailed_help = DETAILED_HELP