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:
Sylvain Bauza 2014-01-15 12:26:56 +01:00
parent aaf7c93ae6
commit 7d2d13ff4d
2 changed files with 178 additions and 7 deletions

View File

@ -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

View File

@ -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):