Merge "Improve variable names in strategy implementations"

This commit is contained in:
Jenkins
2016-02-18 10:22:50 +00:00
committed by Gerrit Code Review
4 changed files with 123 additions and 110 deletions

View File

@@ -64,10 +64,10 @@ class BaseStrategy(object):
self._osc = osc self._osc = osc
@abc.abstractmethod @abc.abstractmethod
def execute(self, model): def execute(self, original_model):
"""Execute a strategy """Execute a strategy
:param model: The name of the strategy to execute (loaded dynamically) :param original_model: The model the strategy is executed on
:type model: str :type model: str
:return: A computed solution (via a placement algorithm) :return: A computed solution (via a placement algorithm)
:rtype: :class:`watcher.decision_engine.solution.base.BaseSolution` :rtype: :class:`watcher.decision_engine.solution.base.BaseSolution`

View File

@@ -109,8 +109,8 @@ class BasicConsolidation(base.BaseStrategy):
return self._ceilometer return self._ceilometer
@ceilometer.setter @ceilometer.setter
def ceilometer(self, c): def ceilometer(self, ceilometer):
self._ceilometer = c self._ceilometer = ceilometer
def compute_attempts(self, size_cluster): def compute_attempts(self, size_cluster):
"""Upper bound of the number of migration """Upper bound of the number of migration
@@ -119,13 +119,13 @@ class BasicConsolidation(base.BaseStrategy):
""" """
self.migration_attempts = size_cluster * self.bound_migration self.migration_attempts = size_cluster * self.bound_migration
def check_migration(self, model, def check_migration(self, cluster_data_model,
src_hypervisor, src_hypervisor,
dest_hypervisor, dest_hypervisor,
vm_to_mig): vm_to_mig):
"""check if the migration is possible """check if the migration is possible
:param model: the current state of the cluster :param cluster_data_model: the current state of the cluster
:param src_hypervisor: the current node of the virtual machine :param src_hypervisor: the current node of the virtual machine
:param dest_hypervisor: the destination of the virtual machine :param dest_hypervisor: the destination of the virtual machine
:param vm_to_mig: the virtual machine :param vm_to_mig: the virtual machine
@@ -142,28 +142,32 @@ class BasicConsolidation(base.BaseStrategy):
total_cores = 0 total_cores = 0
total_disk = 0 total_disk = 0
total_mem = 0 total_mem = 0
cap_cores = model.get_resource_from_id(resource.ResourceType.cpu_cores) cpu_capacity = cluster_data_model.get_resource_from_id(
cap_disk = model.get_resource_from_id(resource.ResourceType.disk) resource.ResourceType.cpu_cores)
cap_mem = model.get_resource_from_id(resource.ResourceType.memory) disk_capacity = cluster_data_model.get_resource_from_id(
resource.ResourceType.disk)
memory_capacity = cluster_data_model.get_resource_from_id(
resource.ResourceType.memory)
for vm_id in model.get_mapping().get_node_vms(dest_hypervisor): for vm_id in cluster_data_model. \
vm = model.get_vm_from_id(vm_id) get_mapping().get_node_vms(dest_hypervisor):
total_cores += cap_cores.get_capacity(vm) vm = cluster_data_model.get_vm_from_id(vm_id)
total_disk += cap_disk.get_capacity(vm) total_cores += cpu_capacity.get_capacity(vm)
total_mem += cap_mem.get_capacity(vm) total_disk += disk_capacity.get_capacity(vm)
total_mem += memory_capacity.get_capacity(vm)
# capacity requested by hypervisor # capacity requested by hypervisor
total_cores += cap_cores.get_capacity(vm_to_mig) total_cores += cpu_capacity.get_capacity(vm_to_mig)
total_disk += cap_disk.get_capacity(vm_to_mig) total_disk += disk_capacity.get_capacity(vm_to_mig)
total_mem += cap_mem.get_capacity(vm_to_mig) total_mem += memory_capacity.get_capacity(vm_to_mig)
return self.check_threshold(model, return self.check_threshold(cluster_data_model,
dest_hypervisor, dest_hypervisor,
total_cores, total_cores,
total_disk, total_disk,
total_mem) total_mem)
def check_threshold(self, model, def check_threshold(self, cluster_data_model,
dest_hypervisor, dest_hypervisor,
total_cores, total_cores,
total_disk, total_disk,
@@ -173,23 +177,23 @@ class BasicConsolidation(base.BaseStrategy):
check the threshold value defined by the ratio of check the threshold value defined by the ratio of
aggregated CPU capacity of VMs on one node to CPU capacity aggregated CPU capacity of VMs on one node to CPU capacity
of this node must not exceed the threshold value. of this node must not exceed the threshold value.
:param dest_hypervisor: :param cluster_data_model: the current state of the cluster
:param dest_hypervisor: the destination of the virtual machine
:param total_cores :param total_cores
:param total_disk :param total_disk
:param total_mem :param total_mem
:return: True if the threshold is not exceed :return: True if the threshold is not exceed
""" """
cap_cores = model.get_resource_from_id(resource.ResourceType.cpu_cores) cpu_capacity = cluster_data_model.get_resource_from_id(
cap_disk = model.get_resource_from_id(resource.ResourceType.disk) resource.ResourceType.cpu_cores).get_capacity(dest_hypervisor)
cap_mem = model.get_resource_from_id(resource.ResourceType.memory) disk_capacity = cluster_data_model.get_resource_from_id(
# available resource.ResourceType.disk).get_capacity(dest_hypervisor)
cores_available = cap_cores.get_capacity(dest_hypervisor) memory_capacity = cluster_data_model.get_resource_from_id(
disk_available = cap_disk.get_capacity(dest_hypervisor) resource.ResourceType.memory).get_capacity(dest_hypervisor)
mem_available = cap_mem.get_capacity(dest_hypervisor)
if cores_available >= total_cores * self.threshold_cores \ if (cpu_capacity >= total_cores * self.threshold_cores and
and disk_available >= total_disk * self.threshold_disk \ disk_capacity >= total_disk * self.threshold_disk and
and mem_available >= total_mem * self.threshold_mem: memory_capacity >= total_mem * self.threshold_mem):
return True return True
else: else:
return False return False
@@ -215,24 +219,25 @@ class BasicConsolidation(base.BaseStrategy):
def get_number_of_migrations(self): def get_number_of_migrations(self):
return self.number_of_migrations return self.number_of_migrations
def calculate_weight(self, model, element, total_cores_used, def calculate_weight(self, cluster_data_model, element,
total_disk_used, total_memory_used): total_cores_used, total_disk_used,
total_memory_used):
"""Calculate weight of every resource """Calculate weight of every resource
:param model: :param cluster_data_model:
:param element: :param element:
:param total_cores_used: :param total_cores_used:
:param total_disk_used: :param total_disk_used:
:param total_memory_used: :param total_memory_used:
:return: :return:
""" """
cpu_capacity = model.get_resource_from_id( cpu_capacity = cluster_data_model.get_resource_from_id(
resource.ResourceType.cpu_cores).get_capacity(element) resource.ResourceType.cpu_cores).get_capacity(element)
disk_capacity = model.get_resource_from_id( disk_capacity = cluster_data_model.get_resource_from_id(
resource.ResourceType.disk).get_capacity(element) resource.ResourceType.disk).get_capacity(element)
memory_capacity = model.get_resource_from_id( memory_capacity = cluster_data_model.get_resource_from_id(
resource.ResourceType.memory).get_capacity(element) resource.ResourceType.memory).get_capacity(element)
score_cores = (1 - (float(cpu_capacity) - float(total_cores_used)) / score_cores = (1 - (float(cpu_capacity) - float(total_cores_used)) /
@@ -259,25 +264,25 @@ class BasicConsolidation(base.BaseStrategy):
:return: :return:
""" """
resource_id = "%s_%s" % (hypervisor.uuid, hypervisor.hostname) resource_id = "%s_%s" % (hypervisor.uuid, hypervisor.hostname)
cpu_avg_vm = self.ceilometer. \ vm_avg_cpu_util = self.ceilometer. \
statistic_aggregation(resource_id=resource_id, statistic_aggregation(resource_id=resource_id,
meter_name=self.HOST_CPU_USAGE_METRIC_NAME, meter_name=self.HOST_CPU_USAGE_METRIC_NAME,
period="7200", period="7200",
aggregate='avg' aggregate='avg'
) )
if cpu_avg_vm is None: if vm_avg_cpu_util is None:
LOG.error( LOG.error(
_LE("No values returned by %(resource_id)s " _LE("No values returned by %(resource_id)s "
"for %(metric_name)s"), "for %(metric_name)s"),
resource_id=resource_id, resource_id=resource_id,
metric_name=self.HOST_CPU_USAGE_METRIC_NAME, metric_name=self.HOST_CPU_USAGE_METRIC_NAME,
) )
cpu_avg_vm = 100 vm_avg_cpu_util = 100
cpu_capacity = model.get_resource_from_id( cpu_capacity = model.get_resource_from_id(
resource.ResourceType.cpu_cores).get_capacity(hypervisor) resource.ResourceType.cpu_cores).get_capacity(hypervisor)
total_cores_used = cpu_capacity * (cpu_avg_vm / 100) total_cores_used = cpu_capacity * (vm_avg_cpu_util / 100)
return self.calculate_weight(model, hypervisor, total_cores_used, return self.calculate_weight(model, hypervisor, total_cores_used,
0, 0,
@@ -295,14 +300,14 @@ class BasicConsolidation(base.BaseStrategy):
else: else:
return 0 return 0
def calculate_score_vm(self, vm, model): def calculate_score_vm(self, vm, cluster_data_model):
"""Calculate Score of virtual machine """Calculate Score of virtual machine
:param vm: the virtual machine :param vm: the virtual machine
:param model: the model :param cluster_data_model: the cluster model
:return: score :return: score
""" """
if model is None: if cluster_data_model is None:
raise exception.ClusterStateNotDefined() raise exception.ClusterStateNotDefined()
vm_cpu_utilization = self.ceilometer. \ vm_cpu_utilization = self.ceilometer. \
@@ -321,14 +326,13 @@ class BasicConsolidation(base.BaseStrategy):
) )
vm_cpu_utilization = 100 vm_cpu_utilization = 100
cpu_capacity = model.get_resource_from_id( cpu_capacity = cluster_data_model.get_resource_from_id(
resource.ResourceType.cpu_cores).get_capacity(vm) resource.ResourceType.cpu_cores).get_capacity(vm)
total_cores_used = cpu_capacity * (vm_cpu_utilization / 100.0) total_cores_used = cpu_capacity * (vm_cpu_utilization / 100.0)
return self.calculate_weight(model, vm, total_cores_used, return self.calculate_weight(cluster_data_model, vm,
0, total_cores_used, 0, 0)
0)
def add_change_service_state(self, resource_id, state): def add_change_service_state(self, resource_id, state):
parameters = {'state': state} parameters = {'state': state}
@@ -348,14 +352,16 @@ class BasicConsolidation(base.BaseStrategy):
resource_id=resource_id, resource_id=resource_id,
input_parameters=parameters) input_parameters=parameters)
def score_of_nodes(self, current_model, score): def score_of_nodes(self, cluster_data_model, score):
"""Calculate score of nodes based on load by VMs""" """Calculate score of nodes based on load by VMs"""
for hypervisor_id in current_model.get_all_hypervisors(): for hypervisor_id in cluster_data_model.get_all_hypervisors():
hypervisor = current_model.get_hypervisor_from_id(hypervisor_id) hypervisor = cluster_data_model. \
count = current_model.get_mapping(). \ get_hypervisor_from_id(hypervisor_id)
count = cluster_data_model.get_mapping(). \
get_node_vms_from_id(hypervisor_id) get_node_vms_from_id(hypervisor_id)
if len(count) > 0: if len(count) > 0:
result = self.calculate_score_node(hypervisor, current_model) result = self.calculate_score_node(hypervisor,
cluster_data_model)
else: else:
''' the hypervisor has not VMs ''' ''' the hypervisor has not VMs '''
result = 0 result = 0
@@ -363,9 +369,9 @@ class BasicConsolidation(base.BaseStrategy):
score.append((hypervisor_id, result)) score.append((hypervisor_id, result))
return score return score
def node_and_vm_score(self, s, score, current_model): def node_and_vm_score(self, sorted_score, score, current_model):
"""Get List of VMs from Node""" """Get List of VMs from Node"""
node_to_release = s[len(score) - 1][0] node_to_release = sorted_score[len(score) - 1][0]
vms_to_mig = current_model.get_mapping().get_node_vms_from_id( vms_to_mig = current_model.get_mapping().get_node_vms_from_id(
node_to_release) node_to_release)
@@ -395,47 +401,49 @@ class BasicConsolidation(base.BaseStrategy):
OFFLINE.value) OFFLINE.value)
self.number_of_released_nodes += 1 self.number_of_released_nodes += 1
def calculate_m(self, v, current_model, node_to_release, s): def calculate_num_migrations(self, sorted_vms, current_model,
m = 0 node_to_release, sorted_score):
for vm in v: number_migrations = 0
for j in range(0, len(s)): for vm in sorted_vms:
for j in range(0, len(sorted_score)):
mig_vm = current_model.get_vm_from_id(vm[0]) mig_vm = current_model.get_vm_from_id(vm[0])
mig_src_hypervisor = current_model.get_hypervisor_from_id( mig_src_hypervisor = current_model.get_hypervisor_from_id(
node_to_release) node_to_release)
mig_dst_hypervisor = current_model.get_hypervisor_from_id( mig_dst_hypervisor = current_model.get_hypervisor_from_id(
s[j][0]) sorted_score[j][0])
result = self.check_migration(current_model, result = self.check_migration(current_model,
mig_src_hypervisor, mig_src_hypervisor,
mig_dst_hypervisor, mig_vm) mig_dst_hypervisor, mig_vm)
if result is True: if result:
self.create_migration_vm( self.create_migration_vm(
current_model, mig_vm, current_model, mig_vm,
mig_src_hypervisor, mig_dst_hypervisor) mig_src_hypervisor, mig_dst_hypervisor)
m += 1 number_migrations += 1
break break
return m return number_migrations
def unsuccessful_migration_actualization(self, m, unsuccessful_migration): def unsuccessful_migration_actualization(self, number_migrations,
if m > 0: unsuccessful_migration):
self.number_of_migrations += m if number_migrations > 0:
self.number_of_migrations += number_migrations
return 0 return 0
else: else:
return unsuccessful_migration + 1 return unsuccessful_migration + 1
def execute(self, orign_model): def execute(self, original_model):
LOG.info(_LI("Initializing Sercon Consolidation")) LOG.info(_LI("Initializing Sercon Consolidation"))
if orign_model is None: if original_model is None:
raise exception.ClusterStateNotDefined() raise exception.ClusterStateNotDefined()
# todo(jed) clone model # todo(jed) clone model
current_model = orign_model current_model = original_model
self.efficacy = 100 self.efficacy = 100
unsuccessful_migration = 0 unsuccessful_migration = 0
first = True first_migration = True
size_cluster = len(current_model.get_all_hypervisors()) size_cluster = len(current_model.get_all_hypervisors())
if size_cluster == 0: if size_cluster == 0:
raise exception.ClusterEmpty() raise exception.ClusterEmpty()
@@ -453,18 +461,18 @@ class BasicConsolidation(base.BaseStrategy):
OFFLINE.value) OFFLINE.value)
while self.get_allowed_migration_attempts() >= unsuccessful_migration: while self.get_allowed_migration_attempts() >= unsuccessful_migration:
if first is not True: if not first_migration:
self.efficacy = self.calculate_migration_efficacy() self.efficacy = self.calculate_migration_efficacy()
if self.efficacy < float(self.target_efficacy): if self.efficacy < float(self.target_efficacy):
break break
first = False first_migration = False
score = [] score = []
score = self.score_of_nodes(current_model, score) score = self.score_of_nodes(current_model, score)
''' sort compute nodes by Score decreasing ''''' ''' sort compute nodes by Score decreasing '''''
s = sorted(score, reverse=True, key=lambda x: (x[1])) sorted_score = sorted(score, reverse=True, key=lambda x: (x[1]))
LOG.debug("Hypervisor(s) BFD {0}".format(s)) LOG.debug("Hypervisor(s) BFD {0}".format(sorted_score))
''' get Node to be released ''' ''' get Node to be released '''
if len(score) == 0: if len(score) == 0:
@@ -474,17 +482,18 @@ class BasicConsolidation(base.BaseStrategy):
break break
node_to_release, vm_score = self.node_and_vm_score( node_to_release, vm_score = self.node_and_vm_score(
s, score, current_model) sorted_score, score, current_model)
''' sort VMs by Score ''' ''' sort VMs by Score '''
v = sorted(vm_score, reverse=True, key=lambda x: (x[1])) sorted_vms = sorted(vm_score, reverse=True, key=lambda x: (x[1]))
# BFD: Best Fit Decrease # BFD: Best Fit Decrease
LOG.debug("VM(s) BFD {0}".format(v)) LOG.debug("VM(s) BFD {0}".format(sorted_vms))
m = self.calculate_m(v, current_model, node_to_release, s) migrations = self.calculate_num_migrations(
sorted_vms, current_model, node_to_release, sorted_score)
unsuccessful_migration = self.unsuccessful_migration_actualization( unsuccessful_migration = self.unsuccessful_migration_actualization(
m, unsuccessful_migration) migrations, unsuccessful_migration)
infos = { infos = {
"number_of_migrations": self.number_of_migrations, "number_of_migrations": self.number_of_migrations,
"number_of_nodes_released": self.number_of_released_nodes, "number_of_nodes_released": self.number_of_released_nodes,

View File

@@ -34,7 +34,7 @@ class DummyStrategy(base.BaseStrategy):
osc=None): osc=None):
super(DummyStrategy, self).__init__(name, description, osc) super(DummyStrategy, self).__init__(name, description, osc)
def execute(self, model): def execute(self, original_model):
LOG.debug("Executing Dummy strategy") LOG.debug("Executing Dummy strategy")
parameters = {'message': 'hello World'} parameters = {'message': 'hello World'}
self.solution.add_action(action_type=self.NOP, self.solution.add_action(action_type=self.NOP,

View File

@@ -88,25 +88,26 @@ class OutletTempControl(base.BaseStrategy):
def ceilometer(self, c): def ceilometer(self, c):
self._ceilometer = c self._ceilometer = c
def calc_used_res(self, model, hypervisor, cap_cores, cap_mem, cap_disk): def calc_used_res(self, cluster_data_model, hypervisor, cpu_capacity,
memory_capacity, disk_capacity):
'''calculate the used vcpus, memory and disk based on VM flavors''' '''calculate the used vcpus, memory and disk based on VM flavors'''
vms = model.get_mapping().get_node_vms(hypervisor) vms = cluster_data_model.get_mapping().get_node_vms(hypervisor)
vcpus_used = 0 vcpus_used = 0
memory_mb_used = 0 memory_mb_used = 0
disk_gb_used = 0 disk_gb_used = 0
if len(vms) > 0: if len(vms) > 0:
for vm_id in vms: for vm_id in vms:
vm = model.get_vm_from_id(vm_id) vm = cluster_data_model.get_vm_from_id(vm_id)
vcpus_used += cap_cores.get_capacity(vm) vcpus_used += cpu_capacity.get_capacity(vm)
memory_mb_used += cap_mem.get_capacity(vm) memory_mb_used += memory_capacity.get_capacity(vm)
disk_gb_used += cap_disk.get_capacity(vm) disk_gb_used += disk_capacity.get_capacity(vm)
return vcpus_used, memory_mb_used, disk_gb_used return vcpus_used, memory_mb_used, disk_gb_used
def group_hosts_by_outlet_temp(self, model): def group_hosts_by_outlet_temp(self, cluster_data_model):
"""Group hosts based on outlet temp meters""" """Group hosts based on outlet temp meters"""
hypervisors = model.get_all_hypervisors() hypervisors = cluster_data_model.get_all_hypervisors()
size_cluster = len(hypervisors) size_cluster = len(hypervisors)
if size_cluster == 0: if size_cluster == 0:
raise wexc.ClusterEmpty() raise wexc.ClusterEmpty()
@@ -114,7 +115,8 @@ class OutletTempControl(base.BaseStrategy):
hosts_need_release = [] hosts_need_release = []
hosts_target = [] hosts_target = []
for hypervisor_id in hypervisors: for hypervisor_id in hypervisors:
hypervisor = model.get_hypervisor_from_id(hypervisor_id) hypervisor = cluster_data_model.get_hypervisor_from_id(
hypervisor_id)
resource_id = hypervisor.uuid resource_id = hypervisor.uuid
outlet_temp = self.ceilometer.statistic_aggregation( outlet_temp = self.ceilometer.statistic_aggregation(
@@ -136,17 +138,18 @@ class OutletTempControl(base.BaseStrategy):
hosts_target.append(hvmap) hosts_target.append(hvmap)
return hosts_need_release, hosts_target return hosts_need_release, hosts_target
def choose_vm_to_migrate(self, model, hosts): def choose_vm_to_migrate(self, cluster_data_model, hosts):
"""pick up an active vm instance to migrate from provided hosts""" """pick up an active vm instance to migrate from provided hosts"""
for hvmap in hosts: for hvmap in hosts:
mig_src_hypervisor = hvmap['hv'] mig_src_hypervisor = hvmap['hv']
vms_of_src = model.get_mapping().get_node_vms(mig_src_hypervisor) vms_of_src = cluster_data_model.get_mapping().get_node_vms(
mig_src_hypervisor)
if len(vms_of_src) > 0: if len(vms_of_src) > 0:
for vm_id in vms_of_src: for vm_id in vms_of_src:
try: try:
# select the first active VM to migrate # select the first active VM to migrate
vm = model.get_vm_from_id(vm_id) vm = cluster_data_model.get_vm_from_id(vm_id)
if vm.state != vm_state.VMState.ACTIVE.value: if vm.state != vm_state.VMState.ACTIVE.value:
LOG.info(_LE("VM not active, skipped: %s"), LOG.info(_LE("VM not active, skipped: %s"),
vm.uuid) vm.uuid)
@@ -158,44 +161,45 @@ class OutletTempControl(base.BaseStrategy):
return None return None
def filter_dest_servers(self, model, hosts, vm_to_migrate): def filter_dest_servers(self, cluster_data_model, hosts, vm_to_migrate):
"""Only return hosts with sufficient available resources""" """Only return hosts with sufficient available resources"""
cap_cores = model.get_resource_from_id(resource.ResourceType.cpu_cores) cpu_capacity = cluster_data_model.get_resource_from_id(
cap_disk = model.get_resource_from_id(resource.ResourceType.disk) resource.ResourceType.cpu_cores)
cap_mem = model.get_resource_from_id(resource.ResourceType.memory) disk_capacity = cluster_data_model.get_resource_from_id(
resource.ResourceType.disk)
memory_capacity = cluster_data_model.get_resource_from_id(
resource.ResourceType.memory)
required_cores = cap_cores.get_capacity(vm_to_migrate) required_cores = cpu_capacity.get_capacity(vm_to_migrate)
required_disk = cap_disk.get_capacity(vm_to_migrate) required_disk = disk_capacity.get_capacity(vm_to_migrate)
required_mem = cap_mem.get_capacity(vm_to_migrate) required_memory = memory_capacity.get_capacity(vm_to_migrate)
# filter hypervisors without enough resource # filter hypervisors without enough resource
dest_servers = [] dest_servers = []
for hvmap in hosts: for hvmap in hosts:
host = hvmap['hv'] host = hvmap['hv']
# available # available
cores_used, mem_used, disk_used = self.calc_used_res(model, cores_used, mem_used, disk_used = self.calc_used_res(
host, cluster_data_model, host, cpu_capacity, memory_capacity,
cap_cores, disk_capacity)
cap_mem, cores_available = cpu_capacity.get_capacity(host) - cores_used
cap_disk) disk_available = disk_capacity.get_capacity(host) - mem_used
cores_available = cap_cores.get_capacity(host) - cores_used mem_available = memory_capacity.get_capacity(host) - disk_used
disk_available = cap_disk.get_capacity(host) - mem_used
mem_available = cap_mem.get_capacity(host) - disk_used
if cores_available >= required_cores \ if cores_available >= required_cores \
and disk_available >= required_disk \ and disk_available >= required_disk \
and mem_available >= required_mem: and mem_available >= required_memory:
dest_servers.append(hvmap) dest_servers.append(hvmap)
return dest_servers return dest_servers
def execute(self, orign_model): def execute(self, original_model):
LOG.debug("Initializing Outlet temperature strategy") LOG.debug("Initializing Outlet temperature strategy")
if orign_model is None: if original_model is None:
raise wexc.ClusterStateNotDefined() raise wexc.ClusterStateNotDefined()
current_model = orign_model current_model = original_model
hosts_need_release, hosts_target = self.group_hosts_by_outlet_temp( hosts_need_release, hosts_target = self.group_hosts_by_outlet_temp(
current_model) current_model)