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."""
|
||||
message = _("Unsupported client version requested.")
|
||||
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.
|
||||
|
||||
import argparse
|
||||
import datetime
|
||||
import logging
|
||||
import re
|
||||
|
||||
from climateclient import command
|
||||
from climateclient import exception
|
||||
|
||||
|
||||
class ListLeases(command.ListCommand):
|
||||
@ -32,16 +35,178 @@ class ShowLease(command.ShowCommand):
|
||||
|
||||
|
||||
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
|
||||
lease. When service creates reserved resource (Nova-VM, Cinder-volume,
|
||||
etc.) it comes to Climate and creates lease via Python client.
|
||||
def args2body(self, parsed_args):
|
||||
params = {}
|
||||
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')
|
||||
|
||||
"""
|
||||
pass
|
||||
params['reservations'] = []
|
||||
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):
|
||||
|
Loading…
Reference in New Issue
Block a user