Implement Lease creation for Physical reservations
climate lease-create was not available, albeit we need it for the physical reservation usecase. Adding it, e.g. climate lease-create --physical-reservation min=1,max=5,\ hypervisor_properties='["=", "$hypervisor_type", "QEMU"]',\ resource_properties="" test_phys Partially implements blueprint python-client Change-Id: Ia836846c3d25a3dd3ccd3308d40f165253df059d
This commit is contained in:
parent
aaf7c93ae6
commit
7d2d13ff4d
@ -70,3 +70,9 @@ class UnsupportedVersion(ClimateClientException):
|
|||||||
"""Occurs if unsupported client version was requested."""
|
"""Occurs if unsupported client version was requested."""
|
||||||
message = _("Unsupported client version requested.")
|
message = _("Unsupported client version requested.")
|
||||||
code = 406
|
code = 406
|
||||||
|
|
||||||
|
|
||||||
|
class IncorrectLease(ClimateClientException):
|
||||||
|
"""Occurs if lease parameters are incorrect."""
|
||||||
|
message = _("The lease parameters are incorrect.")
|
||||||
|
code = 409
|
||||||
|
@ -14,9 +14,12 @@
|
|||||||
# limitations under the License.
|
# limitations under the License.
|
||||||
|
|
||||||
import argparse
|
import argparse
|
||||||
|
import datetime
|
||||||
import logging
|
import logging
|
||||||
|
import re
|
||||||
|
|
||||||
from climateclient import command
|
from climateclient import command
|
||||||
|
from climateclient import exception
|
||||||
|
|
||||||
|
|
||||||
class ListLeases(command.ListCommand):
|
class ListLeases(command.ListCommand):
|
||||||
@ -32,16 +35,178 @@ class ShowLease(command.ShowCommand):
|
|||||||
|
|
||||||
|
|
||||||
class CreateLease(command.CreateCommand):
|
class CreateLease(command.CreateCommand):
|
||||||
"""Comprehended only for physical reservations.
|
resource = 'lease'
|
||||||
|
log = logging.getLogger(__name__ + '.CreateLease')
|
||||||
|
default_start = datetime.datetime.utcnow()
|
||||||
|
default_end = default_start + datetime.timedelta(days=1)
|
||||||
|
|
||||||
For physical reservations lease is created manually.
|
def get_parser(self, prog_name):
|
||||||
|
parser = super(CreateLease, self).get_parser(prog_name)
|
||||||
|
parser.add_argument(
|
||||||
|
'name', metavar=self.resource.upper(),
|
||||||
|
help='Name for the %s' % self.resource
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
'--start-date',
|
||||||
|
dest='start',
|
||||||
|
help='Time (YYYY-MM-DD HH:MM) UTC TZ for starting the lease '
|
||||||
|
'(default: now)',
|
||||||
|
default=self.default_start
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
'--end-date',
|
||||||
|
dest='end',
|
||||||
|
help='Time (YYYY-MM-DD HH:MM) UTC TZ for ending the lease '
|
||||||
|
'(default: 24h later)',
|
||||||
|
default=self.default_end
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
'--physical-reservation',
|
||||||
|
metavar="<min=int,max=int,hypervisor_properties=str,"
|
||||||
|
"resource_properties=str>",
|
||||||
|
action='append',
|
||||||
|
dest='physical_reservations',
|
||||||
|
help='Create a reservation for physical compute hosts. '
|
||||||
|
'Specify option multiple times to create multiple '
|
||||||
|
'reservations. '
|
||||||
|
'min: minimum number of hosts to reserve. '
|
||||||
|
'max: maximum number of hosts to reserve. '
|
||||||
|
'hypervisor_properties: JSON string, see doc. '
|
||||||
|
'resource_properties: JSON string, see doc. ',
|
||||||
|
default=[]
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
'--reservation',
|
||||||
|
metavar="<key=value>",
|
||||||
|
action='append',
|
||||||
|
dest='reservations',
|
||||||
|
help='key/value pairs for creating a generic reservation. '
|
||||||
|
'Specify option multiple times to create multiple '
|
||||||
|
'reservations. ',
|
||||||
|
default=[]
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
'--event', metavar='<event_type=str,event_date=time>',
|
||||||
|
action='append',
|
||||||
|
dest='events',
|
||||||
|
help='Creates an event with key/value pairs for the lease. '
|
||||||
|
'Specify option multiple times to create multiple events. '
|
||||||
|
'event_type: type of event (e.g. notification). '
|
||||||
|
'event_date: Time for event (YYYY-MM-DD HH:MM) UTC TZ. ',
|
||||||
|
default=[]
|
||||||
|
)
|
||||||
|
return parser
|
||||||
|
|
||||||
For virtual reservations we need id of the reserved resource to create
|
def args2body(self, parsed_args):
|
||||||
lease. When service creates reserved resource (Nova-VM, Cinder-volume,
|
params = {}
|
||||||
etc.) it comes to Climate and creates lease via Python client.
|
if parsed_args.name:
|
||||||
|
params['name'] = parsed_args.name
|
||||||
|
if not isinstance(parsed_args.start, datetime.datetime):
|
||||||
|
try:
|
||||||
|
parsed_args.start = datetime.datetime.strptime(
|
||||||
|
parsed_args.start, '%Y-%m-%d %H:%M')
|
||||||
|
except ValueError:
|
||||||
|
raise exception.IncorrectLease
|
||||||
|
if not isinstance(parsed_args.end, datetime.datetime):
|
||||||
|
try:
|
||||||
|
parsed_args.end = datetime.datetime.strptime(
|
||||||
|
parsed_args.end, '%Y-%m-%d %H:%M')
|
||||||
|
except ValueError:
|
||||||
|
raise exception.IncorrectLease
|
||||||
|
if parsed_args.start > parsed_args.end:
|
||||||
|
raise exception.IncorrectLease
|
||||||
|
params['start'] = datetime.datetime.strftime(parsed_args.start,
|
||||||
|
'%Y-%m-%d %H:%M')
|
||||||
|
params['end'] = datetime.datetime.strftime(parsed_args.end,
|
||||||
|
'%Y-%m-%d %H:%M')
|
||||||
|
|
||||||
"""
|
params['reservations'] = []
|
||||||
pass
|
params['events'] = []
|
||||||
|
|
||||||
|
physical_reservations = []
|
||||||
|
for phys_res_str in parsed_args.physical_reservations:
|
||||||
|
err_msg = ("Invalid physical-reservation argument '%s'. "
|
||||||
|
"Reservation arguments must be of the "
|
||||||
|
"form --physical-reservation <min=int,max=int,"
|
||||||
|
"hypervisor_properties=str,resource_properties=str>"
|
||||||
|
% phys_res_str)
|
||||||
|
phys_res_info = {"min": "", "max": "", "hypervisor_properties": "",
|
||||||
|
"resource_properties": ""}
|
||||||
|
prog = re.compile('^(\w+)=(\w+|\[[^]]+\])(?:,(.+))?$')
|
||||||
|
|
||||||
|
def parse_params(params):
|
||||||
|
match = prog.search(params)
|
||||||
|
if match:
|
||||||
|
self.log.info("Matches: %s", match.groups())
|
||||||
|
k, v = match.group(1, 2)
|
||||||
|
if k in phys_res_info:
|
||||||
|
phys_res_info[k] = v
|
||||||
|
else:
|
||||||
|
raise exception.IncorrectLease(err_msg)
|
||||||
|
if len(match.groups()) == 3 and match.group(3) is not None:
|
||||||
|
parse_params(match.group(3))
|
||||||
|
|
||||||
|
parse_params(phys_res_str)
|
||||||
|
if not phys_res_info['min'] and not phys_res_info['max']:
|
||||||
|
raise exception.IncorrectLease(err_msg)
|
||||||
|
# NOTE(sbauza): The resource type should be conf-driven mapped with
|
||||||
|
# climate.conf file but that's potentially on another
|
||||||
|
# host
|
||||||
|
phys_res_info['resource_type'] = 'physical:host'
|
||||||
|
physical_reservations.append(phys_res_info)
|
||||||
|
if physical_reservations:
|
||||||
|
params['reservations'] += physical_reservations
|
||||||
|
|
||||||
|
reservations = []
|
||||||
|
for res_str in parsed_args.reservations:
|
||||||
|
err_msg = ("Invalid reservation argument '%s'. "
|
||||||
|
"Reservation arguments must be of the "
|
||||||
|
"form --reservation <key=value>"
|
||||||
|
% res_str)
|
||||||
|
res_info = {}
|
||||||
|
for kv_str in res_str.split(","):
|
||||||
|
try:
|
||||||
|
k, v = kv_str.split("=", 1)
|
||||||
|
except ValueError:
|
||||||
|
raise exception.IncorrectLease(err_msg)
|
||||||
|
res_info[k] = v
|
||||||
|
reservations.append(res_info)
|
||||||
|
if reservations:
|
||||||
|
params['reservations'] += reservations
|
||||||
|
|
||||||
|
if not params['reservations']:
|
||||||
|
raise exception.IncorrectLease
|
||||||
|
|
||||||
|
events = []
|
||||||
|
for event_str in parsed_args.events:
|
||||||
|
err_msg = ("Invalid event argument '%s'. "
|
||||||
|
"Event arguments must be of the "
|
||||||
|
"form --event <event_type=str,event_date=time>"
|
||||||
|
% event_str)
|
||||||
|
event_info = {"event_type": "", "event_date": ""}
|
||||||
|
for kv_str in event_str.split(","):
|
||||||
|
try:
|
||||||
|
k, v = kv_str.split("=", 1)
|
||||||
|
except ValueError:
|
||||||
|
raise exception.IncorrectLease(err_msg)
|
||||||
|
if k in event_info:
|
||||||
|
event_info[k] = v
|
||||||
|
else:
|
||||||
|
raise exception.IncorrectLease(err_msg)
|
||||||
|
if not event_info['event_type'] and not event_info['event_date']:
|
||||||
|
raise exception.IncorrectLease(err_msg)
|
||||||
|
event_date = event_info['event_date']
|
||||||
|
try:
|
||||||
|
date = datetime.datetime.strptime(event_date, '%Y-%m-%d %H:%M')
|
||||||
|
event_date = datetime.datetime.strftime(date, '%Y-%m-%d %H:%M')
|
||||||
|
event_info['event_date'] = event_date
|
||||||
|
except ValueError:
|
||||||
|
raise exception.IncorrectLease
|
||||||
|
events.append(event_info)
|
||||||
|
if events:
|
||||||
|
params['events'] = events
|
||||||
|
|
||||||
|
return params
|
||||||
|
|
||||||
|
|
||||||
class UpdateLease(command.UpdateCommand):
|
class UpdateLease(command.UpdateCommand):
|
||||||
|
Loading…
Reference in New Issue
Block a user