From f5630c0de0c3c575f0d4274c40543ae4982aac4c Mon Sep 17 00:00:00 2001 From: Madhu Mohan Nelemane Date: Thu, 1 Sep 2016 12:20:02 +0200 Subject: [PATCH 1/5] Initial Draft to support intelRSD and RedFish APIs --- Gemfile | 1 + .../app/controllers/intelrsd_controller.rb | 190 ++++++++++++++++++ .../app/helpers/redfish_helper.rb | 117 +++++++++++ crowbar_framework/app/models/intelrsd.rb | 19 ++ intelrsd.yml | 31 +++ 5 files changed, 358 insertions(+) create mode 100644 crowbar_framework/app/controllers/intelrsd_controller.rb create mode 100644 crowbar_framework/app/helpers/redfish_helper.rb create mode 100644 crowbar_framework/app/models/intelrsd.rb create mode 100644 intelrsd.yml diff --git a/Gemfile b/Gemfile index 587f6c1719..e57c211837 100644 --- a/Gemfile +++ b/Gemfile @@ -23,6 +23,7 @@ group :development do gem "sprockets-standalone", "~> 1.2.1" gem "sprockets", "~> 2.11.0" gem "rspec", "~> 3.1.0" + gem "rest-client", "~> 2.0" end unless ENV["PACKAGING"] && ENV["PACKAGING"] == "yes" diff --git a/crowbar_framework/app/controllers/intelrsd_controller.rb b/crowbar_framework/app/controllers/intelrsd_controller.rb new file mode 100644 index 0000000000..216e97d788 --- /dev/null +++ b/crowbar_framework/app/controllers/intelrsd_controller.rb @@ -0,0 +1,190 @@ +# +# Copyright 2016, SUSE LINUX GmbH +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +require 'json' + +# An important note on interfacing with Redfish APIS: +# +# * Only specific top level URIs may be assumed, and even these +# may be absent based on the implementation. (for ex: there +# might be no /redfish/v1/Systems collection on something that +# doesn't have compute nodes ) +# * The API will eventually be implemented on a system that breaks +# any data model and hence the URIs must be dynamically discovered +# * The data model represented here using @node_object_list prepares +# a list of all the available Systems along with the properties +# and IDs of available resources in each system. This data model +# needs to be appropriately mapped to the Node object of the system +# where the data model is employed. +# + +class IntelRSDController < ApplicationController + attr_reader :logger, :insecure + + def initialize() + @redfish_client = RedfishHelper::RedfishClient.new('localhost', '8443') + @node_object_list = [] + end + + def get_system_resource_list(sys_id, resource) + resource_list = [] + items = @redfish_client.get_resource("Systems/#{sys_id}/#{resource}") + items["Members"].each do | item | + item_odata_id = item["@odata.id"] + item_id = item_odata_id.split(/\//)[-1] + resource_item = @redfish_client.get_resource("Systems/#{sys_id}/#{resource}/#{item_id}") + resource_list.push(resource_item) + end + return resource_list + end + + def make_node_object_for_system(sys_id) + nodeobject = Hash.new() + nodeobject["System_Id"] = sys_id + ["Processors", "Memory", "MemoryChunks", + "EthernetInterfaces", "Adapters"].each do | resource | + nodeobject["#{resource}"] = get_system_resource_list(sys_id, resource) + end + return nodeobject + end + + def get_systems() + @systems = @redfish_client.get_resource("Systems") + sys_list = [] + + @systems["Members"].each do |member| + odata_id = member["@odata.id"] + sys_id = odata_id.split(/\//)[-1] + sys_list.push(sys_id) + end + return sys_list + end + + def get_system_data(sys_id) + system_data = @redfish_client.get_resource("Systems/#{sys_id}") + system_object = make_node_object_for_system(sys_id) + ["Processors", "Memory", "MemoryChunks", + "EthernetInterfaces", "Adapters"].each do | resource | + system_data["#{resource}"] = system_object["#{resource}"] + end + return system_data + end + + def get_rsd_nodes() + system_list = get_systems() + system_list.each do |system| + node_object = make_node_object_for_system(system) + @node_object_list.push(node_object) + end + return @node_object_list + end + + def reset_system(sys_id) + post_action("Systems/#{sys_id}", action: "ComputerSystem.Reset") + end + + def get_crowbar_node_object(sys_id) + system_object = get_system_data(sys_id) + node_name_prefix = "d" + node_name_prefix = "IRSD" if system_object["Oem"].has_key?("Intel_RackScale") + + eth_interface = system_object["EthernetInterfaces"].first + node_name = node_name_prefix + eth_interface["MACAddress"].gsub(":", "-") + + node = NodeObject.create_new node_name + NodeObject.initialize(node) + node.set['name'] = node_name + node.set['target_cpu'] = "" + node.set['target_vendor'] = "suse" + node.set['host_cpu'] = "" + node.set['host_vendor'] = "suse" + node.set['kernel'] = "" # Kernel modules and configurations + node.set['counters'] = "" # various network interfaces and other counters + node.set['hostname'] = node_name + node.set['fqdn'] = node_name + "." + ChefObject.cloud_domain + node.set['domain'] = ChefObject.cloud_domain + ipaddress_data = eth_interface["IPv4Addresses"].first + node.set['ipaddress'] = ipaddress_data["Address"] + node.set['macaddress'] = eth_interface["MACAddress"] + ip6address_data = eth_interface["IPv6Addresses"].first + node.set['ip6address'] = ip6address_data["Address"] + #node.set['ohai_time'] = "" + node.set['recipes'] = "" + + # Add other roles as seen fit + node.set['roles'] = [] + ["deployer-config-default", "network-config-default", "dns-config-default", + "logging-config-default", "ntp-config-default", "nova-compute-kvm", + "provisioner-base", "provisioner-config-default", "crowbar-#{node['fqdn']}"].each do |role_name| + role = RoleObject.find_role_by_name "#{role_name}" + node['roles'] += role + end + + node.set['run_list'] = ["role[#{node['roles']}]"] + node.set['keys']['host']['host_dsa_public'] = "" + node.set['keys']['host']['host_rsa_public'] = "" + node.set['keys']['host']['host_ecdsa_public'] = "" + node.set['virtualization']['system'] = "kvm" + node.set['virtualization']['role'] = "guest" + node.set['platform'] = "suse" + node.set['platform_version'] = "12.1" + node.set['dmi']['bios']['all_records'] = "" + node.set['dmi']['bios']['vendor'] = "" + node.set['dmi']['bios']['version'] = system_object["BiosVersion"] + node.set['dmi']['bios']['release_date'] = "" + node.set['dmi']['bios']['address'] = "" + node.set['dmi']['bios']['runtime_size'] = "" + node.set['dmi']['bios']['rom_size'] = "" + node.set['dmi']['bios']['bios_revision'] = "" + node.set['dmi']['system']['product_name'] = "" + node.set['dmi']['system']['manufacturer'] = "" + node.set['dmi']['system']['serial_number'] = "Not Specified" + node.set['dmi']['system']['uuid'] = "" + node.set['dmi']['system']['wake_up_type'] = "Power Switch" + node.set['dmi']['system']['sku_number'] = "Not Specified" + node.set['dmi']['system']['family'] = "Not Specified" + node.set['dmi']['chassis']['serial_number'] = system_object['Chassis']['SerialNumber'] + node.set['dmi']['chassis']['all_records'] = "" + node.set['dmi']['chassis']['manufacturer'] = "" + node.set['dmi']['chassis']['all_records'] = "" + node.set['dmi']['chassis']['boot_up_state'] = "Safe" + node.set['dmi']['chassis']['power_supply_state'] = "Safe" + node.set['block_device']['sda'] = "" + node.set['memory']['swap'] = "" + node.set['memory']['buffers'] = "" + + system_object["Processors"].each do | processor | + id = processor["Id"] + node.set["cpu"]["#{id}"]['manufacturer'] = processor["Manufacturer"] + node.set["cpu"]["#{id}"]["model"] = processor["Model"] + node.set["cpu"]["#{id}"]["family"] = processor["ProcessorArchitecture"] + node.set["cpu"]["#{id}"]["family"] = "x86_64" if processor["InstructionSet"] == "x86-64" + node.set["cpu"]["#{id}"]["flags"] = processor["Capabilities"] + end + + node.set['filesystem']['sysfs'] = "" + node.save + end +end + +# usage of the controller APIs +rsd_controller = IntelRSDController.new +node_list = rsd_controller.get_rsd_nodes() +first_node = node_list.first +p "FIRST NODE: #{first_node}" +# node_object = rsd_controller.get_crowbar_node_object(first_node["System_Id"]) +# p node_object + diff --git a/crowbar_framework/app/helpers/redfish_helper.rb b/crowbar_framework/app/helpers/redfish_helper.rb new file mode 100644 index 0000000000..1384d60235 --- /dev/null +++ b/crowbar_framework/app/helpers/redfish_helper.rb @@ -0,0 +1,117 @@ +# +# Copyright 2016, SUSE LINUX GmbH +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +require 'json' +require 'uri' +require 'net/http' +require 'rest-client' + +module RedfishHelper + class RedfishClient + attr_reader :logger + + # Standard JSONRPC Error responses + INVALID_JSON = -32700 + INVALID_REQUEST = -32600 + INVALID_PARAMS = -32602 + METHOD_NOT_FOUND = -32601 + INTERNAL_ERROR = -32603 + + # RedFish-specific constants + REDFISH_VERSION = "redfish/v1/" + + def initialize(host, port, insecure=true, client_cert=false) + @service_uri = "https://#{host}:#{port}/#{REDFISH_VERSION}" + @verify_ssl = OpenSSL::SSL::VERIFY_NONE if insecure + @ssl_client_cert = false unless client_cert + end + + def handle_exception(json_rpc_error) + logger.error(json_rpc_error[:message]) + end + + def post_action(resource, action:None, params: None) + uri = @service_uri + resource + uri += "/Actions/#{action}" if action + + begin + response = RestClient::Request.execute(url: uri, + method: :post, + verify_ssl: @verify_ssl, + ssl_client_cert: @ssl_client_cert) + rescue + handle_exception(response) + end + return JSON.parse(response) + end + + def get_resource(resource) + uri = @service_uri + resource + p "QUERYING RESOURCE: #{uri}" + + begin + response = RestClient::Request.execute(url: uri, + method: :get, + verify_ssl: @verify_ssl, + ssl_client_cert: @ssl_client_cert) + rescue + handle_exception(response) + end + + return JSON.parse(response) + end + end +end + +# Usage Examples for this client library + +# Create a client object +redfish_client = RedfishHelper::RedfishClient.new("localhost", "8443") + +# Check if the Redfish Service responds ( returns redfish/v1) +api_resp = redfish_client.get_resource("") +p api_resp + +# Check one of the API responses +systems = redfish_client.get_resource("Systems") +p systems + +sys_list = [] + +# Loop to run through all available systems and populate a node-object +systems["Members"].each do |member| + p "MEMBER DATA: #{member}" + member_id= member["@odata.id"] + p "MEMBER ID: #{member_id}" + sys_id = member_id.split(/\//)[-1] + p "SYSTEM ID: #{sys_id}" + sys_data = Hash.new() + sys_data["Systems"] = redfish_client.get_resource("Systems/#{sys_id}") + p "SYSTEMS DATA: #{sys_data["Systems"]}" + sys_data["Processors"] = redfish_client.get_resource("Systems/#{sys_id}/Processors/1") + p "PROCESSORS DATA: #{sys_data["Processors"]}" + sys_data["Memory"] = redfish_client.get_resource("Systems/#{sys_id}/Memory/1") + p "MEMORY DATA: #{sys_data["Memory"]}" + sys_data["MemoryChunks"] = redfish_client.get_resource("Systems/#{sys_id}/MemoryChunks/1") + p "MEMORY CHUNKS DATA: #{sys_data["MemoryChunks"]}" + sys_data["EthernetInterfaces"] = redfish_client.get_resource("Systems/#{sys_id}/EthernetInterfaces/1") + p "ETHERNET DATA: #{sys_data["EthernetInterfaces"]}" + sys_data["Adapters"] = redfish_client.get_resource("Systems/#{sys_id}/Adapters/1") + p "ADAPTERS DATA: #{sys_data["Adapters"]}" + sys_list.push(sys_data) +end + +p "NODE OBJECT From Redfish : #{sys_list}" diff --git a/crowbar_framework/app/models/intelrsd.rb b/crowbar_framework/app/models/intelrsd.rb new file mode 100644 index 0000000000..ce94a4fa75 --- /dev/null +++ b/crowbar_framework/app/models/intelrsd.rb @@ -0,0 +1,19 @@ +# +# Copyright 2016, SUSE LINUX GmbH +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +class IntelRSD < ActiveResource::Base + attr_accessor :url +end diff --git a/intelrsd.yml b/intelrsd.yml new file mode 100644 index 0000000000..9cbd4e7197 --- /dev/null +++ b/intelrsd.yml @@ -0,0 +1,31 @@ +# +# Copyright 2016, SUSE LINUX GmbH +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +barclamp: + name: intelrsd + display: Intel RackScale Design + description: Integration with Intel Rack Scale Design + version: 1 + user_managed: false + member: + - crowbar + +crowbar: + layout: 1 + order: 112 + run_order: 112 + chef_order: 112 + proposal_schema_version: 3 From 4f32630620e7e1e7c9cda2258c943a35e29403c3 Mon Sep 17 00:00:00 2001 From: Madhu Mohan Nelemane Date: Fri, 30 Sep 2016 10:41:34 +0000 Subject: [PATCH 2/5] UI for node allocation from Intel Rackscale systems - Lists available systems from Rackscale Simulator - Allows selection of specific systems for allocation - Creates a node object and allocates the same as a crowbar node - Also refined the controller to obtain system-specific info - UI contains only a list of check-boxes and one Allocate button --- .../app/controllers/intelrsd_controller.rb | 222 +++++++++++------- .../app/views/rsd/show.html.haml | 32 +++ .../config/locales/intel_rsd/en.yml | 36 +++ crowbar_framework/config/navigation.rb | 1 + .../config/routes.d/intel-rsd.routes | 18 ++ intelrsd.yml | 6 + 6 files changed, 226 insertions(+), 89 deletions(-) create mode 100644 crowbar_framework/app/views/rsd/show.html.haml create mode 100644 crowbar_framework/config/locales/intel_rsd/en.yml create mode 100644 crowbar_framework/config/routes.d/intel-rsd.routes diff --git a/crowbar_framework/app/controllers/intelrsd_controller.rb b/crowbar_framework/app/controllers/intelrsd_controller.rb index 216e97d788..e7ede17c54 100644 --- a/crowbar_framework/app/controllers/intelrsd_controller.rb +++ b/crowbar_framework/app/controllers/intelrsd_controller.rb @@ -31,12 +31,32 @@ # where the data model is employed. # -class IntelRSDController < ApplicationController - attr_reader :logger, :insecure +class RsdController < ApplicationController + attr_reader :redfish_client, :logger + + # Client setup for the class + host = ENV["CROWBAR_REDFISH_HOST"] || "localhost" + port = ENV["CROWBAR_REDFISH_PORT"] || "8443" + @redfish_client = RedfishHelper::RedfishClient.new(host, port) + + def show + @title = "Welcome to RackScale Design" + sys_list = get_all_systems + @rsd_systems = "Systems not Available" + unless sys_list.empty? + @rsd_systems = sys_list + end + end - def initialize() - @redfish_client = RedfishHelper::RedfishClient.new('localhost', '8443') - @node_object_list = [] + def allocate + all_sys_list = get_systems + all_sys_list.each do |sys_id| + next unless params[sys_id.to_s] == "1" + node = get_crowbar_node_object(sys_id) + node.allocate + node.set_state("ready") + end + redirect_to rsd_show_path, notice: "Selected nodes allocated as compute nodes" end def get_system_resource_list(sys_id, resource) @@ -61,7 +81,37 @@ def make_node_object_for_system(sys_id) return nodeobject end - def get_systems() + def get_processors(sys_id) + proc_list = get_system_resource_list(sys_id, "Processors") + processors = [] + proc_list.each do |proc| + proc_object = Hash.new + proc_object["Model"] = proc["Model"] + proc_object["Manufacturer"] = proc["Manufacturer"] + proc_object["Architecture"] = proc["Architecture"] + proc_object["TotalCores"] = proc["TotalCores"] + proc_object["TotalThreads"] = proc["TotalThreads"] + processors.push(proc_object) + end + processors + end + + def get_memory(sys_id) + mem_list = get_system_resource_list(sys_id, "Memory") + memories = [] + mem_list.each do |mem| + mem_object = Hash.new + mem_object["MemoryType"] = mem["MemoryType"] + mem_object["CapacityMB"] = mem["CapacityMiB"] + mem_object["Speed"] = mem["OperatingSpeedMHz"] + mem_object["Size"] = mem["SizeMiB"] + mem_object["Health"] = mem["Health"] + memories.push(mem_object) + end + memories + end + + def get_systems @systems = @redfish_client.get_resource("Systems") sys_list = [] @@ -83,39 +133,46 @@ def get_system_data(sys_id) return system_data end - def get_rsd_nodes() - system_list = get_systems() - system_list.each do |system| - node_object = make_node_object_for_system(system) - @node_object_list.push(node_object) + def get_all_systems + sys_list = get_systems + all_systems = [] + sys_list.each do |sys_id| + sys_object = Hash.new + sys_object["SystemId"] = sys_id + sys_object["Processors"] = get_processors(sys_id) + sys_object["Memory"] = get_memory(sys_id) + all_systems.push(sys_object) end - return @node_object_list - end - - def reset_system(sys_id) - post_action("Systems/#{sys_id}", action: "ComputerSystem.Reset") + all_systems end def get_crowbar_node_object(sys_id) system_object = get_system_data(sys_id) node_name_prefix = "d" - node_name_prefix = "IRSD" if system_object["Oem"].has_key?("Intel_RackScale") + node_name_prefix = "IRSD-" if system_object["Oem"].key?("Intel_RackScale") + # Pickin up the first IP address. This may not be always the correct address. + # It must be revisited when testing with Rackscale hardware. eth_interface = system_object["EthernetInterfaces"].first - node_name = node_name_prefix + eth_interface["MACAddress"].gsub(":", "-") - - node = NodeObject.create_new node_name - NodeObject.initialize(node) - node.set['name'] = node_name - node.set['target_cpu'] = "" - node.set['target_vendor'] = "suse" - node.set['host_cpu'] = "" - node.set['host_vendor'] = "suse" - node.set['kernel'] = "" # Kernel modules and configurations - node.set['counters'] = "" # various network interfaces and other counters - node.set['hostname'] = node_name - node.set['fqdn'] = node_name + "." + ChefObject.cloud_domain - node.set['domain'] = ChefObject.cloud_domain + node_name = node_name_prefix + eth_interface["MACAddress"].tr(":", "-") + "-#{sys_id}" + + node = NodeObject.create_new "#{node_name}.#{Crowbar::Settings.domain}".downcase + + node.set["name"] = node_name + # set a flag to identify this node as a rackscale one + node.set["rackscale"] = true + # track the rackscale id for this node + node.set["rackscale_id"] = sys_id + node.set["target_cpu"] = "x86_64" + node.set["target_vendor"] = "suse" + node.set["host_cpu"] = system_object["ProcessorSummary"]["Model"] + node.set["host_vendor"] = "suse" + node.set["kernel"] = "" # Kernel modules and configurations + node.set["counters"] = "" # various network interfaces and other counters + node.set["hostname"] = node_name + node.set["fqdn"] = "#{node_name}.#{Crowbar::Settings.domain}" + node.set["domain"] = Crowbar::Settings.domain + ipaddress_data = eth_interface["IPv4Addresses"].first node.set['ipaddress'] = ipaddress_data["Address"] node.set['macaddress'] = eth_interface["MACAddress"] @@ -125,66 +182,53 @@ def get_crowbar_node_object(sys_id) node.set['recipes'] = "" # Add other roles as seen fit - node.set['roles'] = [] - ["deployer-config-default", "network-config-default", "dns-config-default", - "logging-config-default", "ntp-config-default", "nova-compute-kvm", - "provisioner-base", "provisioner-config-default", "crowbar-#{node['fqdn']}"].each do |role_name| - role = RoleObject.find_role_by_name "#{role_name}" - node['roles'] += role - end - - node.set['run_list'] = ["role[#{node['roles']}]"] - node.set['keys']['host']['host_dsa_public'] = "" - node.set['keys']['host']['host_rsa_public'] = "" - node.set['keys']['host']['host_ecdsa_public'] = "" - node.set['virtualization']['system'] = "kvm" - node.set['virtualization']['role'] = "guest" - node.set['platform'] = "suse" - node.set['platform_version'] = "12.1" - node.set['dmi']['bios']['all_records'] = "" - node.set['dmi']['bios']['vendor'] = "" - node.set['dmi']['bios']['version'] = system_object["BiosVersion"] - node.set['dmi']['bios']['release_date'] = "" - node.set['dmi']['bios']['address'] = "" - node.set['dmi']['bios']['runtime_size'] = "" - node.set['dmi']['bios']['rom_size'] = "" - node.set['dmi']['bios']['bios_revision'] = "" - node.set['dmi']['system']['product_name'] = "" - node.set['dmi']['system']['manufacturer'] = "" - node.set['dmi']['system']['serial_number'] = "Not Specified" - node.set['dmi']['system']['uuid'] = "" - node.set['dmi']['system']['wake_up_type'] = "Power Switch" - node.set['dmi']['system']['sku_number'] = "Not Specified" - node.set['dmi']['system']['family'] = "Not Specified" - node.set['dmi']['chassis']['serial_number'] = system_object['Chassis']['SerialNumber'] - node.set['dmi']['chassis']['all_records'] = "" - node.set['dmi']['chassis']['manufacturer'] = "" - node.set['dmi']['chassis']['all_records'] = "" - node.set['dmi']['chassis']['boot_up_state'] = "Safe" - node.set['dmi']['chassis']['power_supply_state'] = "Safe" - node.set['block_device']['sda'] = "" - node.set['memory']['swap'] = "" - node.set['memory']['buffers'] = "" - - system_object["Processors"].each do | processor | - id = processor["Id"] - node.set["cpu"]["#{id}"]['manufacturer'] = processor["Manufacturer"] - node.set["cpu"]["#{id}"]["model"] = processor["Model"] - node.set["cpu"]["#{id}"]["family"] = processor["ProcessorArchitecture"] - node.set["cpu"]["#{id}"]["family"] = "x86_64" if processor["InstructionSet"] == "x86-64" - node.set["cpu"]["#{id}"]["flags"] = processor["Capabilities"] + node.set["roles"] = [] + ["deployer-config-default", "network-config-default", "dns-config-default", + "logging-config-default", "ntp-config-default", + "provisioner-base", "provisioner-config-default", "nova-compute"].each do |role_name| + node["roles"] << role_name + end + + node.set["run_list"] = ["role[crowbar-#{node_name}.#{Crowbar::Settings.domain.tr(".", "_")}]"] + node.set["keys"]["host"]["host_dsa_public"] = "" + node.set["keys"]["host"]["host_rsa_public"] = "" + node.set["keys"]["host"]["host_ecdsa_public"] = "" + node.set["virtualization"]["system"] = "kvm" + node.set["virtualization"]["role"] = "guest" + node.set["platform"] = "suse" + node.set["platform_version"] = "12.1" + node.set["dmi"]["bios"]["version"] = system_object["BiosVersion"] + node.set["dmi"]["system"]["product_name"] = system_object["Model"] + node.set["dmi"]["system"]["manufacturer"] = system_object["Manufacturer"] + node.set["dmi"]["system"]["serial_number"] = system_object["SerialNumber"] + node.set["dmi"]["system"]["uuid"] = system_object["UUID"] + node.set["dmi"]["system"]["wake_up_type"] = "Power Switch" + node.set["dmi"]["system"]["sku_number"] = "Not Specified" + node.set["dmi"]["system"]["family"] = "Not Specified" + node.set["dmi"]["chassis"]["serial_number"] = system_object["SerialNumber"] + node.set["dmi"]["chassis"]["boot_up_state"] = "Safe" + node.set["dmi"]["chassis"]["power_supply_state"] = "Safe" + # this is needed so its counted properly for the UI + node.set["block_device"]["sda"] = { removable: "0" } + node.set["memory"]["swap"] = "" + node.set["memory"]["buffers"] = "" + total_mem = 0 + system_object["Memory"].each do |m| + total_mem += m["CapacityMiB"].to_i + end + node.set["memory"]["total"] = "#{total_mem * 1024}kB" + + system_object["Processors"].each do |processor| + id = processor["Id"].to_i - 1 # API starts at 1, we start at 0 + node.set["cpu"][id.to_s]["manufacturer"] = processor["Manufacturer"] + node.set["cpu"][id.to_s]["model"] = processor["Model"] + node.set["cpu"][id.to_s]["family"] = processor["ProcessorArchitecture"] + node.set["cpu"][id.to_s]["family"] = "x86_64" if processor["InstructionSet"] == "x86-64" + node.set["cpu"][id.to_s]["flags"] = processor["Capabilities"] end - node.set['filesystem']['sysfs'] = "" + node.set["filesystem"]["sysfs"] = "" node.save + node end end - -# usage of the controller APIs -rsd_controller = IntelRSDController.new -node_list = rsd_controller.get_rsd_nodes() -first_node = node_list.first -p "FIRST NODE: #{first_node}" -# node_object = rsd_controller.get_crowbar_node_object(first_node["System_Id"]) -# p node_object - diff --git a/crowbar_framework/app/views/rsd/show.html.haml b/crowbar_framework/app/views/rsd/show.html.haml new file mode 100644 index 0000000000..2941897d3f --- /dev/null +++ b/crowbar_framework/app/views/rsd/show.html.haml @@ -0,0 +1,32 @@ +.row + .col-xs-12 + %h1.page-header + = t(".title") + +.row + .col-xs-12 + .panel.panel-default + .panel-body + .alert.alert-info + = t(".rsd_header") + += form_for :node, :url => rsd_allocate_path, :html => { :role => "form" } do |f| + = hidden_field_tag "return", @allocated + .panel.panel-default#accordion + %h2 + = t(".sys_header") + .panel-panel-body + %table.table.table-hover.table-middle{ :style => "border: 1px; width: 100%" } + %thead + %tr + %th + = t(".rsd_selection") + %th + = t(".system_id") + %tbody + - @rsd_systems.each do |rsd_system| + %tr + %td= check_box_tag "#{rsd_system['SystemId']}" + %td= link_to("System-#{rsd_system['SystemId']}") + .btn-group.pull-right + %input.btn.btn-default{ :type => "submit", :name => "allocate", :value => t(".allocate_switch") } diff --git a/crowbar_framework/config/locales/intel_rsd/en.yml b/crowbar_framework/config/locales/intel_rsd/en.yml new file mode 100644 index 0000000000..bb50a9cb1f --- /dev/null +++ b/crowbar_framework/config/locales/intel_rsd/en.yml @@ -0,0 +1,36 @@ +# +# Copyright 2016, SUSE LINUX GmbH +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +en: + nav: + utils: + rsd: 'Intel RackScale' + rsd: + show: + title: 'Intel Rackscale' + rsd_header: 'Lists Systems available from Intel RackScale server by talking to the Redfish APIs' + man_header: 'Managers' + sys_header: 'Systems' + sw_header: 'Switches' + ch_header: 'Chassis' + allocate_switch: 'Allocate' + rsd_selection: 'Selection' + system_id: 'System ID' + barclamp: + rsd: + login: + provide_creds: 'Please provide Rackscale login credentials.' + please_login: 'No active Rackscale session; please login.' diff --git a/crowbar_framework/config/navigation.rb b/crowbar_framework/config/navigation.rb index 38db393bb6..b39b97b3f4 100644 --- a/crowbar_framework/config/navigation.rb +++ b/crowbar_framework/config/navigation.rb @@ -39,6 +39,7 @@ level2.item :repositories, t("nav.utils.repositories"), repositories_path level2.item :backup, t("nav.utils.backup"), backups_path level2.item :logs, t("nav.utils.logs"), utils_path + level2.item :rsd, t("nav.utils.rsd"), rsd_show_path end end end diff --git a/crowbar_framework/config/routes.d/intel-rsd.routes b/crowbar_framework/config/routes.d/intel-rsd.routes new file mode 100644 index 0000000000..078c16fe52 --- /dev/null +++ b/crowbar_framework/config/routes.d/intel-rsd.routes @@ -0,0 +1,18 @@ +# +# Copyright 2016, SUSE LINUX GmbH +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +get 'rsd/show' => 'rsd#show', as: 'rsd_show' +post 'rsd/allocate' => 'rsd#allocate', as: 'rsd_allocate' diff --git a/intelrsd.yml b/intelrsd.yml index 9cbd4e7197..ba73cf8b70 100644 --- a/intelrsd.yml +++ b/intelrsd.yml @@ -29,3 +29,9 @@ crowbar: run_order: 112 chef_order: 112 proposal_schema_version: 3 + +nav: + utils: + rsd: + order: 91 + route: 'rsd_edit_path' From 29f7888b173dd663ef8e85557467c3bbf4cc6b95 Mon Sep 17 00:00:00 2001 From: Itxaka Date: Wed, 5 Oct 2016 11:20:00 +0200 Subject: [PATCH 3/5] Use host and port for rest server from the environment, fallback to localhost and default port On node creation: - set the proper node+domain for the node object - set the node name to the short name - set the fqdn and domain name to the proper ones - set the uptime to avoid failure on node details UI - set the roles to just names so they are linked to the barclamps - set the product name, serial number so they are shown properly in the UI - set the disk key for removable so disks are shown properly on the UI - count all the memory and aggregate it to be shown on the UI - count the processors with an 0 index so they can be shown in the UI - allocate the node - set the node to ready status All the node creation changes also fix the node not being shown properly when going to the details as well as crowbar crashing when moving the node to a different group --- .../app/controllers/intelrsd_controller.rb | 169 ++++++++---------- .../app/helpers/redfish_helper.rb | 91 +++------- crowbar_framework/app/models/intelrsd.rb | 2 +- 3 files changed, 101 insertions(+), 161 deletions(-) diff --git a/crowbar_framework/app/controllers/intelrsd_controller.rb b/crowbar_framework/app/controllers/intelrsd_controller.rb index e7ede17c54..ecb8f13007 100644 --- a/crowbar_framework/app/controllers/intelrsd_controller.rb +++ b/crowbar_framework/app/controllers/intelrsd_controller.rb @@ -14,22 +14,23 @@ # limitations under the License. # -require 'json' +require "json" # An important note on interfacing with Redfish APIS: # -# * Only specific top level URIs may be assumed, and even these -# may be absent based on the implementation. (for ex: there -# might be no /redfish/v1/Systems collection on something that -# doesn't have compute nodes ) -# * The API will eventually be implemented on a system that breaks -# any data model and hence the URIs must be dynamically discovered -# * The data model represented here using @node_object_list prepares -# a list of all the available Systems along with the properties -# and IDs of available resources in each system. This data model -# needs to be appropriately mapped to the Node object of the system -# where the data model is employed. -# +# Only specific top level URIs may be assumed, and even these +# may be absent based on the implementation. (for ex: there +# might be no /redfish/v1/Systems collection on something that +# doesn't have compute nodes ) +# +# The API will eventually be implemented on a system that breaks +# any data model and hence the URIs must be dynamically discovered +# The data model represented here using @node_object_list prepares +# a list of all the available Systems along with the properties +# and IDs of available resources in each system. This data model +# needs to be appropriately mapped to the Node object of the system +# where the data model is employed. +# class RsdController < ApplicationController attr_reader :redfish_client, :logger @@ -48,67 +49,33 @@ def show end end - def allocate - all_sys_list = get_systems - all_sys_list.each do |sys_id| - next unless params[sys_id.to_s] == "1" - node = get_crowbar_node_object(sys_id) - node.allocate - node.set_state("ready") - end - redirect_to rsd_show_path, notice: "Selected nodes allocated as compute nodes" + def initialize + host = ENV["CROWBAR_REDFISH_HOST"] || "localhost" + port = ENV["CROWBAR_REDFISH_PORT"] || "8443" + @redfish_client = RedfishHelper::RedfishClient.new(host, port) + @node_object_list = [] end def get_system_resource_list(sys_id, resource) resource_list = [] items = @redfish_client.get_resource("Systems/#{sys_id}/#{resource}") - items["Members"].each do | item | + items["Members"].each do |item| item_odata_id = item["@odata.id"] - item_id = item_odata_id.split(/\//)[-1] + item_id = item_odata_id.split("/")[-1] resource_item = @redfish_client.get_resource("Systems/#{sys_id}/#{resource}/#{item_id}") resource_list.push(resource_item) end - return resource_list + resource_list end def make_node_object_for_system(sys_id) - nodeobject = Hash.new() + nodeobject = Hash.new nodeobject["System_Id"] = sys_id - ["Processors", "Memory", "MemoryChunks", - "EthernetInterfaces", "Adapters"].each do | resource | - nodeobject["#{resource}"] = get_system_resource_list(sys_id, resource) - end - return nodeobject - end - - def get_processors(sys_id) - proc_list = get_system_resource_list(sys_id, "Processors") - processors = [] - proc_list.each do |proc| - proc_object = Hash.new - proc_object["Model"] = proc["Model"] - proc_object["Manufacturer"] = proc["Manufacturer"] - proc_object["Architecture"] = proc["Architecture"] - proc_object["TotalCores"] = proc["TotalCores"] - proc_object["TotalThreads"] = proc["TotalThreads"] - processors.push(proc_object) - end - processors - end - - def get_memory(sys_id) - mem_list = get_system_resource_list(sys_id, "Memory") - memories = [] - mem_list.each do |mem| - mem_object = Hash.new - mem_object["MemoryType"] = mem["MemoryType"] - mem_object["CapacityMB"] = mem["CapacityMiB"] - mem_object["Speed"] = mem["OperatingSpeedMHz"] - mem_object["Size"] = mem["SizeMiB"] - mem_object["Health"] = mem["Health"] - memories.push(mem_object) + ["Processors", "Memory", "MemoryChunks", + "EthernetInterfaces", "Adapters"].each do |resource| + nodeobject[resource.to_s] = get_system_resource_list(sys_id, resource) end - memories + nodeobject end def get_systems @@ -117,75 +84,70 @@ def get_systems @systems["Members"].each do |member| odata_id = member["@odata.id"] - sys_id = odata_id.split(/\//)[-1] + sys_id = odata_id.split("/")[-1] sys_list.push(sys_id) end - return sys_list + sys_list end def get_system_data(sys_id) system_data = @redfish_client.get_resource("Systems/#{sys_id}") system_object = make_node_object_for_system(sys_id) ["Processors", "Memory", "MemoryChunks", - "EthernetInterfaces", "Adapters"].each do | resource | - system_data["#{resource}"] = system_object["#{resource}"] + "EthernetInterfaces", "Adapters"].each do |resource| + system_data[resource.to_s] = system_object[resource.to_s] end - return system_data + system_data end - def get_all_systems - sys_list = get_systems - all_systems = [] - sys_list.each do |sys_id| - sys_object = Hash.new - sys_object["SystemId"] = sys_id - sys_object["Processors"] = get_processors(sys_id) - sys_object["Memory"] = get_memory(sys_id) - all_systems.push(sys_object) + def get_rsd_nodes + system_list = get_systems + system_list.each do |system| + node_object = make_node_object_for_system(system) + @node_object_list.push(node_object) end - all_systems + @node_object_list + end + + def reset_system(sys_id) + post_action("Systems/#{sys_id}", action: "ComputerSystem.Reset") end def get_crowbar_node_object(sys_id) system_object = get_system_data(sys_id) node_name_prefix = "d" - node_name_prefix = "IRSD-" if system_object["Oem"].key?("Intel_RackScale") + node_name_prefix = "IRSD" if system_object["Oem"].key?("Intel_RackScale") # Pickin up the first IP address. This may not be always the correct address. # It must be revisited when testing with Rackscale hardware. eth_interface = system_object["EthernetInterfaces"].first - node_name = node_name_prefix + eth_interface["MACAddress"].tr(":", "-") + "-#{sys_id}" + node_name = node_name_prefix + eth_interface["MACAddress"].tr(":", "-") node = NodeObject.create_new "#{node_name}.#{Crowbar::Settings.domain}".downcase node.set["name"] = node_name - # set a flag to identify this node as a rackscale one - node.set["rackscale"] = true - # track the rackscale id for this node - node.set["rackscale_id"] = sys_id - node.set["target_cpu"] = "x86_64" + node.set["target_cpu"] = "" node.set["target_vendor"] = "suse" - node.set["host_cpu"] = system_object["ProcessorSummary"]["Model"] + node.set["host_cpu"] = "" node.set["host_vendor"] = "suse" node.set["kernel"] = "" # Kernel modules and configurations node.set["counters"] = "" # various network interfaces and other counters node.set["hostname"] = node_name node.set["fqdn"] = "#{node_name}.#{Crowbar::Settings.domain}" node.set["domain"] = Crowbar::Settings.domain - ipaddress_data = eth_interface["IPv4Addresses"].first - node.set['ipaddress'] = ipaddress_data["Address"] - node.set['macaddress'] = eth_interface["MACAddress"] + node.set["ipaddress"] = ipaddress_data["Address"] + node.set["macaddress"] = eth_interface["MACAddress"] ip6address_data = eth_interface["IPv6Addresses"].first - node.set['ip6address'] = ip6address_data["Address"] - #node.set['ohai_time'] = "" - node.set['recipes'] = "" + node.set["ip6address"] = ip6address_data["Address"] + node.set["uptime"] = "1 minute" + node.set["recipes"] = "" # Add other roles as seen fit node.set["roles"] = [] ["deployer-config-default", "network-config-default", "dns-config-default", "logging-config-default", "ntp-config-default", - "provisioner-base", "provisioner-config-default", "nova-compute"].each do |role_name| + "provisioner-base", "provisioner-config-default"].each do |role_name| node["roles"] << role_name end @@ -197,15 +159,25 @@ def get_crowbar_node_object(sys_id) node.set["virtualization"]["role"] = "guest" node.set["platform"] = "suse" node.set["platform_version"] = "12.1" + node.set["dmi"]["bios"]["all_records"] = "" + node.set["dmi"]["bios"]["vendor"] = "" node.set["dmi"]["bios"]["version"] = system_object["BiosVersion"] + node.set["dmi"]["bios"]["release_date"] = "" + node.set["dmi"]["bios"]["address"] = "" + node.set["dmi"]["bios"]["runtime_size"] = "" + node.set["dmi"]["bios"]["rom_size"] = "" + node.set["dmi"]["bios"]["bios_revision"] = "" node.set["dmi"]["system"]["product_name"] = system_object["Model"] - node.set["dmi"]["system"]["manufacturer"] = system_object["Manufacturer"] - node.set["dmi"]["system"]["serial_number"] = system_object["SerialNumber"] - node.set["dmi"]["system"]["uuid"] = system_object["UUID"] + node.set["dmi"]["system"]["manufacturer"] = "" + node.set["dmi"]["system"]["serial_number"] = "Not Specified" + node.set["dmi"]["system"]["uuid"] = "" node.set["dmi"]["system"]["wake_up_type"] = "Power Switch" node.set["dmi"]["system"]["sku_number"] = "Not Specified" node.set["dmi"]["system"]["family"] = "Not Specified" node.set["dmi"]["chassis"]["serial_number"] = system_object["SerialNumber"] + node.set["dmi"]["chassis"]["all_records"] = "" + node.set["dmi"]["chassis"]["manufacturer"] = "" + node.set["dmi"]["chassis"]["all_records"] = "" node.set["dmi"]["chassis"]["boot_up_state"] = "Safe" node.set["dmi"]["chassis"]["power_supply_state"] = "Safe" # this is needed so its counted properly for the UI @@ -229,6 +201,15 @@ def get_crowbar_node_object(sys_id) node.set["filesystem"]["sysfs"] = "" node.save - node + node.allocate + node.set_state("ready") end end + +# run it on a thread to not block the UI at the start +Thread.new do + rsd_controller = IntelRSDController.new + node_list = rsd_controller.get_systems + first_node = node_list.first + rsd_controller.get_crowbar_node_object(first_node) +end diff --git a/crowbar_framework/app/helpers/redfish_helper.rb b/crowbar_framework/app/helpers/redfish_helper.rb index 1384d60235..28d3167aee 100644 --- a/crowbar_framework/app/helpers/redfish_helper.rb +++ b/crowbar_framework/app/helpers/redfish_helper.rb @@ -14,104 +14,63 @@ # limitations under the License. # -require 'json' -require 'uri' -require 'net/http' -require 'rest-client' +require "json" +require "uri" +require "net/http" +require "rest-client" module RedfishHelper class RedfishClient attr_reader :logger - + # Standard JSONRPC Error responses INVALID_JSON = -32700 INVALID_REQUEST = -32600 INVALID_PARAMS = -32602 METHOD_NOT_FOUND = -32601 INTERNAL_ERROR = -32603 - + # RedFish-specific constants - REDFISH_VERSION = "redfish/v1/" - - def initialize(host, port, insecure=true, client_cert=false) + REDFISH_VERSION = "redfish/v1/".freeze + + def initialize(host, port, insecure = true, client_cert = false) @service_uri = "https://#{host}:#{port}/#{REDFISH_VERSION}" @verify_ssl = OpenSSL::SSL::VERIFY_NONE if insecure @ssl_client_cert = false unless client_cert end - + def handle_exception(json_rpc_error) - logger.error(json_rpc_error[:message]) + Rails.logger.error(json_rpc_error[:message]) end - + def post_action(resource, action:None, params: None) uri = @service_uri + resource uri += "/Actions/#{action}" if action - + begin response = RestClient::Request.execute(url: uri, - method: :post, - verify_ssl: @verify_ssl, - ssl_client_cert: @ssl_client_cert) + method: :post, + verify_ssl: @verify_ssl, + ssl_client_cert: @ssl_client_cert) rescue handle_exception(response) end - return JSON.parse(response) + JSON.parse(response) end - + def get_resource(resource) uri = @service_uri + resource - p "QUERYING RESOURCE: #{uri}" - + Rails.logger.debug("QUERYING RESOURCE: #{uri}") + begin - response = RestClient::Request.execute(url: uri, - method: :get, - verify_ssl: @verify_ssl, - ssl_client_cert: @ssl_client_cert) + response = RestClient::Request.execute(url: uri, + method: :get, + verify_ssl: @verify_ssl, + ssl_client_cert: @ssl_client_cert) rescue handle_exception(response) end - - return JSON.parse(response) + JSON.parse(response) end end end - -# Usage Examples for this client library - -# Create a client object -redfish_client = RedfishHelper::RedfishClient.new("localhost", "8443") - -# Check if the Redfish Service responds ( returns redfish/v1) -api_resp = redfish_client.get_resource("") -p api_resp - -# Check one of the API responses -systems = redfish_client.get_resource("Systems") -p systems - -sys_list = [] - -# Loop to run through all available systems and populate a node-object -systems["Members"].each do |member| - p "MEMBER DATA: #{member}" - member_id= member["@odata.id"] - p "MEMBER ID: #{member_id}" - sys_id = member_id.split(/\//)[-1] - p "SYSTEM ID: #{sys_id}" - sys_data = Hash.new() - sys_data["Systems"] = redfish_client.get_resource("Systems/#{sys_id}") - p "SYSTEMS DATA: #{sys_data["Systems"]}" - sys_data["Processors"] = redfish_client.get_resource("Systems/#{sys_id}/Processors/1") - p "PROCESSORS DATA: #{sys_data["Processors"]}" - sys_data["Memory"] = redfish_client.get_resource("Systems/#{sys_id}/Memory/1") - p "MEMORY DATA: #{sys_data["Memory"]}" - sys_data["MemoryChunks"] = redfish_client.get_resource("Systems/#{sys_id}/MemoryChunks/1") - p "MEMORY CHUNKS DATA: #{sys_data["MemoryChunks"]}" - sys_data["EthernetInterfaces"] = redfish_client.get_resource("Systems/#{sys_id}/EthernetInterfaces/1") - p "ETHERNET DATA: #{sys_data["EthernetInterfaces"]}" - sys_data["Adapters"] = redfish_client.get_resource("Systems/#{sys_id}/Adapters/1") - p "ADAPTERS DATA: #{sys_data["Adapters"]}" - sys_list.push(sys_data) -end - -p "NODE OBJECT From Redfish : #{sys_list}" diff --git a/crowbar_framework/app/models/intelrsd.rb b/crowbar_framework/app/models/intelrsd.rb index ce94a4fa75..7fb030ac7c 100644 --- a/crowbar_framework/app/models/intelrsd.rb +++ b/crowbar_framework/app/models/intelrsd.rb @@ -15,5 +15,5 @@ # class IntelRSD < ActiveResource::Base - attr_accessor :url + attr_accessor :url end From f3e290ab04908d6aab115218259ee4fad5820ab4 Mon Sep 17 00:00:00 2001 From: Itxaka Date: Wed, 5 Oct 2016 14:28:04 +0200 Subject: [PATCH 4/5] - Clean up. - Store 2 more items in the NodeObject to identify and track the rackscale instances - Rewrite the rest-client queries to protect and log http exceptions - Create several Power management shortcuts for the RedFish client - Create a handler for redfish commands - On power management actions, check for rackscale and execute the actions via rest calls --- .../app/controllers/intelrsd_controller.rb | 8 +-- .../app/helpers/redfish_helper.rb | 54 +++++++++++++++---- crowbar_framework/app/models/node_object.rb | 51 +++++++++++++++++- 3 files changed, 97 insertions(+), 16 deletions(-) diff --git a/crowbar_framework/app/controllers/intelrsd_controller.rb b/crowbar_framework/app/controllers/intelrsd_controller.rb index ecb8f13007..0fb9470aab 100644 --- a/crowbar_framework/app/controllers/intelrsd_controller.rb +++ b/crowbar_framework/app/controllers/intelrsd_controller.rb @@ -109,10 +109,6 @@ def get_rsd_nodes @node_object_list end - def reset_system(sys_id) - post_action("Systems/#{sys_id}", action: "ComputerSystem.Reset") - end - def get_crowbar_node_object(sys_id) system_object = get_system_data(sys_id) node_name_prefix = "d" @@ -126,6 +122,10 @@ def get_crowbar_node_object(sys_id) node = NodeObject.create_new "#{node_name}.#{Crowbar::Settings.domain}".downcase node.set["name"] = node_name + # set a flag to identify this node as a rackscale one + node.set["rackscale"] = true + # track the rackscale id for this node + node.set["rackscale_id"] = sys_id node.set["target_cpu"] = "" node.set["target_vendor"] = "suse" node.set["host_cpu"] = "" diff --git a/crowbar_framework/app/helpers/redfish_helper.rb b/crowbar_framework/app/helpers/redfish_helper.rb index 28d3167aee..3efecd676d 100644 --- a/crowbar_framework/app/helpers/redfish_helper.rb +++ b/crowbar_framework/app/helpers/redfish_helper.rb @@ -37,25 +37,56 @@ def initialize(host, port, insecure = true, client_cert = false) @service_uri = "https://#{host}:#{port}/#{REDFISH_VERSION}" @verify_ssl = OpenSSL::SSL::VERIFY_NONE if insecure @ssl_client_cert = false unless client_cert + @reset_action = "ComputerSystem.Reset".freeze end - def handle_exception(json_rpc_error) - Rails.logger.error(json_rpc_error[:message]) - end - - def post_action(resource, action:None, params: None) + def post_action(resource, action = nil, payload = nil) uri = @service_uri + resource uri += "/Actions/#{action}" if action + payload = {} unless payload begin response = RestClient::Request.execute(url: uri, method: :post, + payload: payload.to_json, + headers: { content_type: :json }, verify_ssl: @verify_ssl, ssl_client_cert: @ssl_client_cert) - rescue - handle_exception(response) + JSON.parse(response) + rescue RestClient::ExceptionWithResponse => e + Rails.logger.error("Error while trying to post #{payload} to #{uri}: #{e}") + false end - JSON.parse(response) + end + + def restart(resource) + post_action("Systems/#{resource}", + @reset_action, + "ResetType" => "GracefulRestart") + end + + def shutdown(resource) + post_action("Systems/#{resource}", + @reset_action, + "ResetType" => "GracefulShutdown") + end + + def poweron(resource) + post_action("Systems/#{resource}", + @reset_action, + "ResetType" => "On") + end + + def powercycle(resource) + post_action("Systems/#{resource}", + @reset_action, + "ResetType" => "ForceRestart") + end + + def poweroff(resource) + post_action("Systems/#{resource}", + @reset_action, + "ResetType" => "ForceOff") end def get_resource(resource) @@ -67,10 +98,11 @@ def get_resource(resource) method: :get, verify_ssl: @verify_ssl, ssl_client_cert: @ssl_client_cert) - rescue - handle_exception(response) + return JSON.parse(response) + rescue RestClient::ExceptionWithResponse => e + Rails.logger.error(e) + JSON.parse(e.response) end - JSON.parse(response) end end end diff --git a/crowbar_framework/app/models/node_object.rb b/crowbar_framework/app/models/node_object.rb index 46175e161b..fec5d13d02 100644 --- a/crowbar_framework/app/models/node_object.rb +++ b/crowbar_framework/app/models/node_object.rb @@ -1161,6 +1161,43 @@ def bmc_cmd(cmd) [200, {}] end + def redfish_rest_cmd(cmd, rackscale_id) + host = ENV["CROWBAR_REDFISH_HOST"] || "localhost" + port = ENV["CROWBAR_REDFISH_PORT"] || "8443" + redfish_client = RedfishHelper::RedfishClient.new(host, port) + case cmd + when :reboot + unless redfish_client.restart(rackscale_id) + Rails.logger.warn("rackscale reboot rest cmd failed - node in unknown state") + [422, I18n.t("unknown_state", scope: "error")] + end + when :shutdown + unless redfish_client.shutdown(rackscale_id) + Rails.logger.warn("rackscale shutdown rest cmd failed - node in unknown state") + [422, I18n.t("unknown_state", scope: "error")] + end + when :poweron + unless redfish_client.poweron(rackscale_id) + Rails.logger.warn("rackscale poweron rest cmd failed - node in unknown state") + [422, I18n.t("unknown_state", scope: "error")] + end + when :powercycle + unless redfish_client.powercycle(rackscale_id) + Rails.logger.warn("rackscale powercycle rest cmd failed - node in unknown state") + [422, I18n.t("unknown_state", scope: "error")] + end + when :poweroff + unless redfish_client.poweroff(rackscale_id) + Rails.logger.warn("rackscale poweroff rest cmd failed - node in unknown state") + [422, I18n.t("unknown_state", scope: "error")] + end + else + Rails.logger.warn("Unknown command #{cmd} for #{@node.name}.") + [400, I18n.t("unknown_cmd", scope: "error", cmd: cmd)] + end + [200, {}] + end + def set_state(state) # use the real transition function for this cb = CrowbarService.new Rails.logger @@ -1261,6 +1298,8 @@ def reboot set_state("reboot") if @node[:platform_family] == "windows" net_rpc_cmd(:reboot) + elsif @node["rackscale"] + redfish_rest_cmd(:reboot, @node["rackscale_id"]) else ssh_cmd("/sbin/reboot") end @@ -1270,6 +1309,8 @@ def shutdown set_state("shutdown") if @node[:platform_family] == "windows" net_rpc_cmd(:shutdown) + elsif @node["rackscale"] + redfish_rest_cmd(:shutdown, @node["rackscale_id"]) else ssh_cmd("/sbin/poweroff") end @@ -1277,13 +1318,19 @@ def shutdown def poweron set_state("poweron") - bmc_cmd("power on") + if @node["rackscale"] + redfish_rest_cmd(:poweron, @node["rackscale_id"]) + else + bmc_cmd("power on") + end end def powercycle set_state("reboot") if @node[:platform_family] == "windows" net_rpc_cmd(:power_cycle) + elsif @node["rackscale"] + redfish_rest_cmd(:powercycle, @node["rackscale_id"]) else bmc_cmd("power cycle") end @@ -1293,6 +1340,8 @@ def poweroff set_state("shutdown") if @node[:platform_family] == "windows" net_rpc_cmd(:power_off) + elsif @node["rackscale"] + redfish_rest_cmd(:poweroff, @node["rackscale_id"]) else bmc_cmd("power off") end From 73108465229dc35e18d182ac29742d45340a9d91 Mon Sep 17 00:00:00 2001 From: Madhu Mohan Nelemane Date: Fri, 16 Dec 2016 14:43:36 +0100 Subject: [PATCH 5/5] Improvements to Crowbar-UI for Rackscale - Added text boxes to edit PSME server IP and port. --- .../app/controllers/intelrsd_controller.rb | 15 ++++++++---- .../rsd/{show.html.haml => display.html.haml} | 0 .../app/views/rsd/settings.html.haml | 24 +++++++++++++++++++ .../config/locales/intel_rsd/en.yml | 5 ++++ crowbar_framework/config/navigation.rb | 2 +- .../config/routes.d/intel-rsd.routes | 4 +++- intelrsd.yml | 2 +- 7 files changed, 45 insertions(+), 7 deletions(-) rename crowbar_framework/app/views/rsd/{show.html.haml => display.html.haml} (100%) create mode 100644 crowbar_framework/app/views/rsd/settings.html.haml diff --git a/crowbar_framework/app/controllers/intelrsd_controller.rb b/crowbar_framework/app/controllers/intelrsd_controller.rb index 0fb9470aab..5d649d97d5 100644 --- a/crowbar_framework/app/controllers/intelrsd_controller.rb +++ b/crowbar_framework/app/controllers/intelrsd_controller.rb @@ -36,11 +36,18 @@ class RsdController < ApplicationController attr_reader :redfish_client, :logger # Client setup for the class - host = ENV["CROWBAR_REDFISH_HOST"] || "localhost" - port = ENV["CROWBAR_REDFISH_PORT"] || "8443" - @redfish_client = RedfishHelper::RedfishClient.new(host, port) + @host = ENV["CROWBAR_REDFISH_HOST"] || "localhost" + @port = ENV["CROWBAR_REDFISH_PORT"] || "8443" - def show + def settings + @host = @rsd_rest_server + @port = @rsd_server_port + Rails.logger.warn "RackScale Server: #{@host}, Port: #{@port}" + end + + def display + Rails.logger.warn "RackScale Server: #{@host}, Port: #{@port}" + @redfish_client = RedfishHelper::RedfishClient.new(@host, @port) @title = "Welcome to RackScale Design" sys_list = get_all_systems @rsd_systems = "Systems not Available" diff --git a/crowbar_framework/app/views/rsd/show.html.haml b/crowbar_framework/app/views/rsd/display.html.haml similarity index 100% rename from crowbar_framework/app/views/rsd/show.html.haml rename to crowbar_framework/app/views/rsd/display.html.haml diff --git a/crowbar_framework/app/views/rsd/settings.html.haml b/crowbar_framework/app/views/rsd/settings.html.haml new file mode 100644 index 0000000000..a4d4cb5619 --- /dev/null +++ b/crowbar_framework/app/views/rsd/settings.html.haml @@ -0,0 +1,24 @@ +.row + .col-xs-12 + %h1.page-header + = t(".title") + + .btn-group.pull-right + = link_to t(".show"), rsd_display_path, :class => "btn btn-default" + += form_tag rsd_display_path, :role => "form" do |f| + .panel.panel-default + .panel-body + .form-group + %label{ :for => :rsd_rest_server } + = t(".rsd_rest_server") + = text_field_tag :rsd_rest_server, @rsd_rest_server, :class => "form-control", :disabled => nil + + .form-group + %label{ :for => :rsd_server_port } + = t(".rsd_server_port") + = text_field_tag :rsd_server_port, @rsd_server_port, :class => "form-control", :disabled => nil + + .panel-footer.text-right + .btn-group + %input.btn.btn-default{ :type => "submit", :name => "show", :value => t(".show") } diff --git a/crowbar_framework/config/locales/intel_rsd/en.yml b/crowbar_framework/config/locales/intel_rsd/en.yml index bb50a9cb1f..1ea4f8bbda 100644 --- a/crowbar_framework/config/locales/intel_rsd/en.yml +++ b/crowbar_framework/config/locales/intel_rsd/en.yml @@ -19,6 +19,11 @@ en: utils: rsd: 'Intel RackScale' rsd: + settings: + title: 'Intel Rackscale Server Details' + rsd_rest_server: 'PSME Rest Server IP' + rsd_server_port: 'PSME Rest Server Port' + show: 'Show' show: title: 'Intel Rackscale' rsd_header: 'Lists Systems available from Intel RackScale server by talking to the Redfish APIs' diff --git a/crowbar_framework/config/navigation.rb b/crowbar_framework/config/navigation.rb index b39b97b3f4..6634f47cc0 100644 --- a/crowbar_framework/config/navigation.rb +++ b/crowbar_framework/config/navigation.rb @@ -39,7 +39,7 @@ level2.item :repositories, t("nav.utils.repositories"), repositories_path level2.item :backup, t("nav.utils.backup"), backups_path level2.item :logs, t("nav.utils.logs"), utils_path - level2.item :rsd, t("nav.utils.rsd"), rsd_show_path + level2.item :rsd, t("nav.utils.rsd"), rsd_settings_path end end end diff --git a/crowbar_framework/config/routes.d/intel-rsd.routes b/crowbar_framework/config/routes.d/intel-rsd.routes index 078c16fe52..2ea39e527d 100644 --- a/crowbar_framework/config/routes.d/intel-rsd.routes +++ b/crowbar_framework/config/routes.d/intel-rsd.routes @@ -14,5 +14,7 @@ # limitations under the License. # -get 'rsd/show' => 'rsd#show', as: 'rsd_show' +post 'rsd/display' => 'rsd#display', as: 'rsd_display' +get 'rsd/settings' => 'rsd#settings', as: 'rsd_settings' +post 'rsd/show' => 'rsd#show', as: 'rsd_show' post 'rsd/allocate' => 'rsd#allocate', as: 'rsd_allocate' diff --git a/intelrsd.yml b/intelrsd.yml index ba73cf8b70..4350b27ada 100644 --- a/intelrsd.yml +++ b/intelrsd.yml @@ -34,4 +34,4 @@ nav: utils: rsd: order: 91 - route: 'rsd_edit_path' + route: 'rsd_settings_path'