diff --git a/concho/config.py b/concho/config.py index 700673c..a6fdcf7 100644 --- a/concho/config.py +++ b/concho/config.py @@ -17,6 +17,7 @@ class Chassis(Item): self.num_cpu_slots_per_server = 2 if re.match('dell-poweredge-r9.*', uid): self.num_cpu_slots_per_server = 4 + self.num_dimm_slots_per_channel = 2 class Dimm(Item): @@ -34,7 +35,7 @@ class Cpu(Item): super().__init__(proc_id) cpuTable = numpy.genfromtxt('cpu_table.dat', dtype=("|U32", float, int, float, float, float), names=True, delimiter='\t') for cpu_id, clock, num_cores, tdp, cpumark in zip(cpuTable['id'], cpuTable['clock'], cpuTable['num_cores'], cpuTable['tdp'], cpuTable['cpumark_1_cpu']): - print(cpu_id) + # print(cpu_id) if cpu_id == proc_id: # print('found '+procId) break @@ -174,6 +175,15 @@ def get_simd_id(proc_arch): 'coffeelake':'avx2' }[proc_arch] +class MemChannel(): + + def __init__(self): + self.dimms = [] + +class CpuSlotMem(): + + def __init__(self): + self.mem_channels = [] class Config(): @@ -182,7 +192,8 @@ class Config(): self.num_servers = 0 self.num_cpu_per_server = 0 self.cpu = None - self.dimms_per_channel = [] + self.cpu_slots_mem = [] + @property def chassis(self): @@ -236,16 +247,39 @@ class Config(): # dimm_capacity = 16 dimm = self.configurator.get_dimm(dimm_capacity) assert dimm - self.dimms_per_channel = [ dimm ] + print(cpu.uid, cpu.num_cores, ram_per_channel) + for cpu_slot_mem in self.cpu_slots_mem: + for mem_channel in cpu_slot_mem.mem_channels: + for dimm_slot_index in range(self.configurator.chassis.item.num_dimm_slots_per_channel): + mem_channel.dimms[dimm_slot_index] = dimm + + @property + def ram_size(self): + ram_size = 0 + for cpu_slot_mem in self.cpu_slots_mem: + for mem_channel in cpu_slot_mem.mem_channels: + for dimm_slot_index in range(self.configurator.chassis.item.num_dimm_slots_per_channel): + dimm = mem_channel.dimms[dimm_slot_index] + if dimm is not None: + dimm = self.configurator.get_item(dimm.uid) + ram_size += self.num_servers * dimm.num_gb + return ram_size + + @property + def ram_price(self): + ram_price = 0.0 + for cpu_slot_mem in self.cpu_slots_mem: + for mem_channel in cpu_slot_mem.mem_channels: + for dimm_slot_index in range(self.configurator.chassis.item.num_dimm_slots_per_channel): + dimm = mem_channel.dimms[dimm_slot_index] + if dimm is not None: + dimm_price = self.configurator.get_item_price(dimm.uid) + ram_price += self.num_servers * dimm_price + return ram_price - def get_price(self): price = self.configurator.chassis.price - price += self.num_servers * self.num_cpu_per_server * self.configurator.get_item_price(self.cpu.uid) - for dimm in self.dimms_per_channel: - dimm_price = self.configurator.get_item_price(dimm.uid) - price += self.num_servers * self.num_cpu_per_server * self.cpu.num_ram_channels * dimm_price - # print("ram_price : %f € for %d dimms of %d Gb" % (ram_price, self.num_servers * self.num_cpu_per_server * cpu.num_ram_channels, dimm_capacity)) + price += self.num_servers * self.num_cpu_per_server * self.configurator.get_item_price(self.cpu.uid) + self.ram_price assert price > 0.0 return price @@ -261,7 +295,21 @@ class Config(): def set_cpu(self, cpu): self.cpu = cpu + # create the dimm slots + self.cpu_slots_mem = [] + for cpu_index in range(self.num_cpu_per_server): + cpu_slot_mem = CpuSlotMem() + + for channel_index in range(cpu.num_ram_channels): + mem_channel = MemChannel() + for dimm_slot_index in range(self.configurator.chassis.item.num_dimm_slots_per_channel): + mem_channel.dimms.append(None) # dimm slots are empty + cpu_slot_mem.mem_channels.append(mem_channel) + self.cpu_slots_mem.append(cpu_slot_mem) + @property + def num_cpus(self): + return self.num_cpu_per_server * self.num_servers class Option(): @@ -305,6 +353,11 @@ class Configurator(): return dimm assert False, 'failed to find an option for a dimm of capacity %d gb' % dimm_capacity + def get_item(self, item_uid): + for module in self.modules.values(): + if item_uid in module.options: + return module.options[item_uid].item + def get_item_price(self, item_uid): for module in self.modules.values(): if item_uid in module.options: diff --git a/concho/dell.py b/concho/dell.py index 259687b..3ad7e3d 100644 --- a/concho/dell.py +++ b/concho/dell.py @@ -487,11 +487,9 @@ class DellConfiguratorParser(): assert False, 'unhandled label : %s' % label return ram_options - def _parse_base_config(self, html_root, configurator): - base_config = Config(configurator) - base_config.num_servers = configurator.chassis.item.max_num_servers - base_config.num_cpu_per_server = 1 - module_root_element = DellConfiguratorParser._get_module(html_root, 'Processeurs (Passage)') + @classmethod + def _get_module_default_item(cls, module_label, html_root): + module_root_element = DellConfiguratorParser._get_module(html_root, module_label) assert module_root_element is not None for option_root_element in module_root_element.xpath(".//div[@class='row']"): label_elements = option_root_element.xpath(".//div[@class='option-selected ']") @@ -499,15 +497,37 @@ class DellConfiguratorParser(): if len(label_elements) > 0: label = label_elements[0].text_content().replace('\n', '') price = DellConfiguratorParser.price_str_as_float(option_root_element.xpath(".//div[@class='col-md-3 text-right option-price option-price-selected']")[0].text_content()) - assert price == 0.0 - # print(label, price) - # Processeur Intel Xeon Silver 4208 2.1GHz,11M Cache,9.60GT/s, 2UPI,No Turbo, HT,8C/16T (85W) - DDR4-2400 - match = re.match(r'^Processeur Intel Xeon (?PSilver|Gold|Platinium) (?P[0-9][0-9][0-9][0-9][RLYU]?).*', label) - assert match, 'unhandled label : %s' % label - # print(match['cpu_class'], match['cpu_number']) - cpu_id = "intel-xeon-%s-%s" % (match['cpu_class'].lower(), match['cpu_number'].lower()) - base_config.set_cpu(Cpu(cpu_id)) - assert base_config.cpu is not None + assert price == 0.0, 'default items are expected to have a price of 0.0 € (%s s price is %f)' % (label, price) + return label + assert False, 'failed to find the default item of module %s' % module_label + + def _parse_base_config(self, html_root, configurator): + base_config = Config(configurator) + base_config.num_servers = configurator.chassis.item.max_num_servers + base_config.num_cpu_per_server = 1 + + # initialize cpu + item_label = DellConfiguratorParser._get_module_default_item('Processeurs (Passage)', html_root) + # Processeur Intel Xeon Silver 4208 2.1GHz,11M Cache,9.60GT/s, 2UPI,No Turbo, HT,8C/16T (85W) - DDR4-2400 + match = re.match(r'^Processeur Intel Xeon (?PSilver|Gold|Platinium) (?P[0-9][0-9][0-9][0-9][RLYU]?).*', item_label) + assert match, 'unhandled label : %s' % item_label + # print(match['cpu_class'], match['cpu_number']) + cpu_id = "intel-xeon-%s-%s" % (match['cpu_class'].lower(), match['cpu_number'].lower()) + base_config.set_cpu(Cpu(cpu_id)) + + # initialize the default ram dimms + item_label = DellConfiguratorParser._get_module_default_item('Mémoires (Passage)', html_root) + # Mémoire 16 Go DDR4 à 2933MHz (1x16Go) + match = re.match(r'^Mémoire (?P[0-9]+) Go DDR4 à (?P[0-9]+)MHz \((?P[0-9]+)x(?P[0-9]+)Go\)', item_label) + assert match, 'unhandled label : %s' % item_label + assert int(match['num_dimms']) == 1 + assert match['num_gb'] == match['num_gb2'] + # print(match['cpu_class'], match['cpu_number']) + cpu_slot_index = 0 + mem_channel_index = 0 + dimm_slot = 0 + base_config.cpu_slots_mem[cpu_slot_index].mem_channels[mem_channel_index].dimms[dimm_slot] = Dimm(num_gb=int(match['num_gb']), num_mhz=int(match['num_mhz']), mem_type='rdimm') + return base_config @@ -570,7 +590,8 @@ class DellConfiguratorParser(): assert price_value_element is not None base_price = DellConfiguratorParser.price_str_as_float(price_value_element.text_content()) assert base_price is not None - configurator.chassis.price = base_price - configurator.get_item_price(configurator.base_config.cpu.uid) + + configurator.chassis.price = base_price - configurator.base_config.num_cpus * configurator.get_item_price(configurator.base_config.cpu.uid) - configurator.base_config.ram_price class DellMatinfoConfigurator(Configurator): @@ -583,5 +604,12 @@ class DellMatinfoConfigurator(Configurator): def create_config(self): - return copy.copy(self.base_config) + # config = copy.deepcopy(self.base_config) + config = Config(self) + config.num_servers = self.base_config.num_servers + config.num_cpu_per_server = self.base_config.num_cpu_per_server + config.set_cpu(self.base_config.cpu) + config.cpu_slots_mem = copy.deepcopy(self.base_config.cpu_slots_mem) + + return config diff --git a/concho/procs_chooser.py b/concho/procs_chooser.py index 490f7be..595f6af 100644 --- a/concho/procs_chooser.py +++ b/concho/procs_chooser.py @@ -76,7 +76,7 @@ def plot_system_efficiency(): def getColorCodeFromItemLabel(label): generation=label[-1] - (model, proc_id) = re.split('_', label) + (model, proc_id, ram_size) = re.split('_', label) saturation = { 'sandy bridge':0.0, 'ivy bridge':0.2, @@ -113,7 +113,7 @@ def plot_system_efficiency(): return matplotlib.colors.hsv_to_rgb((hue, saturation, value)) def get_marker_from_label(label): - (model, proc_id) = re.split('_', label) + (model, proc_id, ram_size) = re.split('_', label) return get_marker(proc_id) @@ -125,14 +125,14 @@ def plot_system_efficiency(): dell.DellMatinfoConfigurator('rcrc1406676-4834664 - Cat2 Conf4 PowerEdge R640 - Dell.html')] # dell.DellPowerEdgeR940()] for configurator in configurators: - config = configurator.create_config() for cpu in configurator.get_cpu_options(): if not cpu.architecture in ['coffeelake', 'skylake','cascadelake']: continue - item_label = numpy.append( item_label, config.chassis.uid + '_' + cpu.uid ) + config = configurator.create_config() config.set_cpu(cpu) config.set_ram(ram_per_core=4.0e9) config.num_cpu_per_server = config.configurator.chassis.item.num_cpu_slots_per_server + item_label = numpy.append( item_label, config.chassis.uid + '_' + cpu.uid + '_' + str(config.ram_size) + 'gb' ) # print('procOptionPrice', procOptionPrice) # config_price = procOptionPrice # config_price += config.get_empty_price() @@ -146,6 +146,7 @@ def plot_system_efficiency(): # print('config.config.get_guarantee_price(5)', config.get_guarantee_price(5)) # config_price += config.get_disk_upgrade_price(2.0e12) # print('config.get_disk_upgrade_price(2.0e12)', config.get_disk_upgrade_price(2.0e12)) + # print(item_label, config.get_price(), config.get_power_consumption()) item_price = numpy.append( item_price, config.get_price() ) item_power_consumption = numpy.append( item_power_consumption, config.get_power_consumption()) # # print(hostTypeId, procId, item_power_consumption[-1])