root / trunk / src / haizea / core / frontends / opennebula.py @ 641
1 |
# -------------------------------------------------------------------------- #
|
---|---|
2 |
# Copyright 2006-2009, University of Chicago #
|
3 |
# Copyright 2008-2009, Distributed Systems Architecture Group, Universidad #
|
4 |
# Complutense de Madrid (dsa-research.org) #
|
5 |
# #
|
6 |
# Licensed under the Apache License, Version 2.0 (the "License"); you may #
|
7 |
# not use this file except in compliance with the License. You may obtain #
|
8 |
# a copy of the License at #
|
9 |
# #
|
10 |
# http://www.apache.org/licenses/LICENSE-2.0 #
|
11 |
# #
|
12 |
# Unless required by applicable law or agreed to in writing, software #
|
13 |
# distributed under the License is distributed on an "AS IS" BASIS, #
|
14 |
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. #
|
15 |
# See the License for the specific language governing permissions and #
|
16 |
# limitations under the License. #
|
17 |
# -------------------------------------------------------------------------- #
|
18 |
|
19 |
import haizea.common.constants as constants |
20 |
from haizea.core.leases import Lease, Capacity, Timestamp, Duration, UnmanagedSoftwareEnvironment |
21 |
from haizea.core.frontends import RequestFrontend |
22 |
from haizea.core.scheduler.slottable import ResourceTuple |
23 |
from haizea.common.utils import UNIX2DateTime, round_datetime, get_config, get_clock |
24 |
from haizea.common.opennebula_xmlrpc import OpenNebulaXMLRPCClient, OpenNebulaVM |
25 |
from mx.DateTime import DateTimeDelta, TimeDelta, ISO |
26 |
|
27 |
import operator |
28 |
import logging |
29 |
|
30 |
one_rpc = None
|
31 |
|
32 |
def get_one_xmlrpcclient(): |
33 |
global one_rpc
|
34 |
if one_rpc == None: |
35 |
host = get_config().get("one.host")
|
36 |
port = get_config().get("one.port")
|
37 |
user, passw = OpenNebulaXMLRPCClient.get_userpass_from_env() |
38 |
one_rpc = OpenNebulaXMLRPCClient(host, port, user, passw) |
39 |
return one_rpc
|
40 |
|
41 |
class OpenNebulaHaizeaVM(object): |
42 |
HAIZEA_PARAM = "HAIZEA"
|
43 |
HAIZEA_START = "START"
|
44 |
HAIZEA_START_NOW = "now"
|
45 |
HAIZEA_START_BESTEFFORT = "best_effort"
|
46 |
HAIZEA_DURATION = "DURATION"
|
47 |
HAIZEA_DURATION_UNLIMITED = "unlimited"
|
48 |
HAIZEA_PREEMPTIBLE = "PREEMPTIBLE"
|
49 |
HAIZEA_PREEMPTIBLE_YES = "yes"
|
50 |
HAIZEA_PREEMPTIBLE_NO = "no"
|
51 |
HAIZEA_GROUP = "GROUP"
|
52 |
|
53 |
|
54 |
def __init__(self, opennebula_vm): |
55 |
# If there is no HAIZEA parameter, the default is to treat the
|
56 |
# request as an immediate request with unlimited duration
|
57 |
if not opennebula_vm.template.has_key(OpenNebulaHaizeaVM.HAIZEA_PARAM): |
58 |
self.start = OpenNebulaHaizeaVM.HAIZEA_START_NOW
|
59 |
self.duration = OpenNebulaHaizeaVM.HAIZEA_DURATION_UNLIMITED
|
60 |
self.preemptible = OpenNebulaHaizeaVM.HAIZEA_PREEMPTIBLE_NO
|
61 |
self.group = None |
62 |
else:
|
63 |
self.start = opennebula_vm.template[OpenNebulaHaizeaVM.HAIZEA_PARAM][OpenNebulaHaizeaVM.HAIZEA_START]
|
64 |
self.duration = opennebula_vm.template[OpenNebulaHaizeaVM.HAIZEA_PARAM][OpenNebulaHaizeaVM.HAIZEA_DURATION]
|
65 |
self.preemptible = opennebula_vm.template[OpenNebulaHaizeaVM.HAIZEA_PARAM][OpenNebulaHaizeaVM.HAIZEA_PREEMPTIBLE]
|
66 |
if opennebula_vm.template[OpenNebulaHaizeaVM.HAIZEA_PARAM].has_key(OpenNebulaHaizeaVM.HAIZEA_GROUP):
|
67 |
self.group = opennebula_vm.template[OpenNebulaHaizeaVM.HAIZEA_PARAM][OpenNebulaHaizeaVM.HAIZEA_GROUP]
|
68 |
else:
|
69 |
self.group = None |
70 |
|
71 |
self.submit_time = UNIX2DateTime(opennebula_vm.stime)
|
72 |
|
73 |
# Create Timestamp object
|
74 |
if self.start == OpenNebulaHaizeaVM.HAIZEA_START_NOW: |
75 |
self.start = Timestamp(Timestamp.NOW)
|
76 |
elif self.start == OpenNebulaHaizeaVM.HAIZEA_START_BESTEFFORT: |
77 |
self.start = Timestamp(Timestamp.UNSPECIFIED)
|
78 |
elif self.start[0] == "+": |
79 |
# Relative time
|
80 |
self.start = Timestamp(round_datetime(self.submit_time + ISO.ParseTime(self.start[1:]))) |
81 |
else:
|
82 |
self.start = Timestamp(ISO.ParseDateTime(self.start)) |
83 |
|
84 |
# Create Duration object
|
85 |
if self.duration == OpenNebulaHaizeaVM.HAIZEA_DURATION_UNLIMITED: |
86 |
# This is an interim solution (make it run for a century).
|
87 |
# TODO: Integrate concept of unlimited duration in the lease datastruct
|
88 |
self.duration = Duration(DateTimeDelta(36500)) |
89 |
else:
|
90 |
self.duration = Duration(ISO.ParseTimeDelta(self.duration)) |
91 |
|
92 |
|
93 |
self.preemptible = (self.preemptible == OpenNebulaHaizeaVM.HAIZEA_PREEMPTIBLE_YES) |
94 |
|
95 |
|
96 |
self.capacity = Capacity([constants.RES_CPU, constants.RES_MEM, constants.RES_DISK])
|
97 |
|
98 |
# CPU
|
99 |
# CPUs in VMs are not reported the same as in hosts.
|
100 |
# THere are two template values: CPU and VCPU.
|
101 |
# CPU reports the percentage of the CPU needed by the VM.
|
102 |
# VCPU, which is optional, reports how many CPUs are needed.
|
103 |
cpu = int(float(opennebula_vm.template["CPU"]) * 100) |
104 |
if opennebula_vm.template.has_key("VCPU"): |
105 |
ncpu = int(opennebula_vm.template["VCPU"]) |
106 |
else:
|
107 |
ncpu = 1
|
108 |
self.capacity.set_ninstances(constants.RES_CPU, ncpu)
|
109 |
for i in range(ncpu): |
110 |
self.capacity.set_quantity_instance(constants.RES_CPU, i+1, cpu) |
111 |
|
112 |
# Memory. Unlike hosts, memory is reported directly in MBs
|
113 |
self.capacity.set_quantity(constants.RES_MEM, int(opennebula_vm.template["MEMORY"])) |
114 |
|
115 |
self.one_id = opennebula_vm.id
|
116 |
|
117 |
|
118 |
class OpenNebulaFrontend(RequestFrontend): |
119 |
|
120 |
def __init__(self, manager): |
121 |
self.manager = manager
|
122 |
self.processed = []
|
123 |
self.logger = logging.getLogger("ONEREQ") |
124 |
self.rpc = get_one_xmlrpcclient()
|
125 |
|
126 |
|
127 |
def get_accumulated_requests(self): |
128 |
vms = self.rpc.vmpool_info()
|
129 |
|
130 |
# Extract the pending OpenNebula VMs
|
131 |
pending_vms = [] |
132 |
for vm in vms: |
133 |
if not vm.id in self.processed and vm.state == OpenNebulaVM.STATE_PENDING: |
134 |
vm_detailed = self.rpc.vm_info(vm.id)
|
135 |
pending_vms.append(OpenNebulaHaizeaVM(vm_detailed)) |
136 |
self.processed.append(vm.id)
|
137 |
|
138 |
grouped = [vm for vm in pending_vms if vm.group != None] |
139 |
not_grouped = [vm for vm in pending_vms if vm.group == None] |
140 |
|
141 |
# Extract VM groups
|
142 |
group_ids = set([vm.group for vm in grouped]) |
143 |
groups = {} |
144 |
for group_id in group_ids: |
145 |
groups[group_id] = [vm for vm in grouped if vm.group == group_id] |
146 |
|
147 |
lease_requests = [] |
148 |
for group_id, opennebula_vms in groups.items(): |
149 |
lease_requests.append(self.__ONEreqs_to_lease(opennebula_vms, group_id))
|
150 |
|
151 |
for opennebula_vm in not_grouped: |
152 |
lease_requests.append(self.__ONEreqs_to_lease([opennebula_vm]))
|
153 |
|
154 |
lease_requests.sort(key=operator.attrgetter("submit_time"))
|
155 |
return lease_requests
|
156 |
|
157 |
def exists_more_requests(self): |
158 |
return True |
159 |
|
160 |
|
161 |
def __ONEreqs_to_lease(self, opennebula_vms, group_id=None): |
162 |
# The vm_with_params is used to extract the HAIZEA parameters.
|
163 |
# (i.e., lease-wide attributes)
|
164 |
vm_with_params = opennebula_vms[0]
|
165 |
|
166 |
# Per-lease attributes
|
167 |
start = vm_with_params.start |
168 |
duration = vm_with_params.duration |
169 |
preemptible = vm_with_params.preemptible |
170 |
submit_time = vm_with_params.submit_time |
171 |
|
172 |
# Per-vnode attributes
|
173 |
requested_resources = dict([(i+1,vm.capacity) for i, vm in enumerate(opennebula_vms)]) |
174 |
|
175 |
lease = Lease.create_new(submit_time = submit_time, |
176 |
requested_resources = requested_resources, |
177 |
start = start, |
178 |
duration = duration, |
179 |
deadline = None,
|
180 |
preemptible = preemptible, |
181 |
software = UnmanagedSoftwareEnvironment()) |
182 |
|
183 |
lease.enactment_info = group_id |
184 |
lease.vnode_enactment_info = dict([(i+1,vm.one_id) for i, vm in enumerate(opennebula_vms)]) |
185 |
return lease
|
186 |
|