Skip to content

Commit f5630c0

Browse files
committed
Initial Draft to support intelRSD and RedFish APIs
1 parent 6bf9735 commit f5630c0

File tree

5 files changed

+358
-0
lines changed

5 files changed

+358
-0
lines changed

Gemfile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ group :development do
2323
gem "sprockets-standalone", "~> 1.2.1"
2424
gem "sprockets", "~> 2.11.0"
2525
gem "rspec", "~> 3.1.0"
26+
gem "rest-client", "~> 2.0"
2627
end
2728

2829
unless ENV["PACKAGING"] && ENV["PACKAGING"] == "yes"
Lines changed: 190 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,190 @@
1+
#
2+
# Copyright 2016, SUSE LINUX GmbH
3+
#
4+
# Licensed under the Apache License, Version 2.0 (the "License");
5+
# you may not use this file except in compliance with the License.
6+
# You may obtain a copy of the License at
7+
#
8+
# http://www.apache.org/licenses/LICENSE-2.0
9+
#
10+
# Unless required by applicable law or agreed to in writing, software
11+
# distributed under the License is distributed on an "AS IS" BASIS,
12+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
# See the License for the specific language governing permissions and
14+
# limitations under the License.
15+
#
16+
17+
require 'json'
18+
19+
# An important note on interfacing with Redfish APIS:
20+
#
21+
# * Only specific top level URIs may be assumed, and even these
22+
# may be absent based on the implementation. (for ex: there
23+
# might be no /redfish/v1/Systems collection on something that
24+
# doesn't have compute nodes )
25+
# * The API will eventually be implemented on a system that breaks
26+
# any data model and hence the URIs must be dynamically discovered
27+
# * The data model represented here using @node_object_list prepares
28+
# a list of all the available Systems along with the properties
29+
# and IDs of available resources in each system. This data model
30+
# needs to be appropriately mapped to the Node object of the system
31+
# where the data model is employed.
32+
#
33+
34+
class IntelRSDController < ApplicationController
35+
attr_reader :logger, :insecure
36+
37+
def initialize()
38+
@redfish_client = RedfishHelper::RedfishClient.new('localhost', '8443')
39+
@node_object_list = []
40+
end
41+
42+
def get_system_resource_list(sys_id, resource)
43+
resource_list = []
44+
items = @redfish_client.get_resource("Systems/#{sys_id}/#{resource}")
45+
items["Members"].each do | item |
46+
item_odata_id = item["@odata.id"]
47+
item_id = item_odata_id.split(/\//)[-1]
48+
resource_item = @redfish_client.get_resource("Systems/#{sys_id}/#{resource}/#{item_id}")
49+
resource_list.push(resource_item)
50+
end
51+
return resource_list
52+
end
53+
54+
def make_node_object_for_system(sys_id)
55+
nodeobject = Hash.new()
56+
nodeobject["System_Id"] = sys_id
57+
["Processors", "Memory", "MemoryChunks",
58+
"EthernetInterfaces", "Adapters"].each do | resource |
59+
nodeobject["#{resource}"] = get_system_resource_list(sys_id, resource)
60+
end
61+
return nodeobject
62+
end
63+
64+
def get_systems()
65+
@systems = @redfish_client.get_resource("Systems")
66+
sys_list = []
67+
68+
@systems["Members"].each do |member|
69+
odata_id = member["@odata.id"]
70+
sys_id = odata_id.split(/\//)[-1]
71+
sys_list.push(sys_id)
72+
end
73+
return sys_list
74+
end
75+
76+
def get_system_data(sys_id)
77+
system_data = @redfish_client.get_resource("Systems/#{sys_id}")
78+
system_object = make_node_object_for_system(sys_id)
79+
["Processors", "Memory", "MemoryChunks",
80+
"EthernetInterfaces", "Adapters"].each do | resource |
81+
system_data["#{resource}"] = system_object["#{resource}"]
82+
end
83+
return system_data
84+
end
85+
86+
def get_rsd_nodes()
87+
system_list = get_systems()
88+
system_list.each do |system|
89+
node_object = make_node_object_for_system(system)
90+
@node_object_list.push(node_object)
91+
end
92+
return @node_object_list
93+
end
94+
95+
def reset_system(sys_id)
96+
post_action("Systems/#{sys_id}", action: "ComputerSystem.Reset")
97+
end
98+
99+
def get_crowbar_node_object(sys_id)
100+
system_object = get_system_data(sys_id)
101+
node_name_prefix = "d"
102+
node_name_prefix = "IRSD" if system_object["Oem"].has_key?("Intel_RackScale")
103+
104+
eth_interface = system_object["EthernetInterfaces"].first
105+
node_name = node_name_prefix + eth_interface["MACAddress"].gsub(":", "-")
106+
107+
node = NodeObject.create_new node_name
108+
NodeObject.initialize(node)
109+
node.set['name'] = node_name
110+
node.set['target_cpu'] = ""
111+
node.set['target_vendor'] = "suse"
112+
node.set['host_cpu'] = ""
113+
node.set['host_vendor'] = "suse"
114+
node.set['kernel'] = "" # Kernel modules and configurations
115+
node.set['counters'] = "" # various network interfaces and other counters
116+
node.set['hostname'] = node_name
117+
node.set['fqdn'] = node_name + "." + ChefObject.cloud_domain
118+
node.set['domain'] = ChefObject.cloud_domain
119+
ipaddress_data = eth_interface["IPv4Addresses"].first
120+
node.set['ipaddress'] = ipaddress_data["Address"]
121+
node.set['macaddress'] = eth_interface["MACAddress"]
122+
ip6address_data = eth_interface["IPv6Addresses"].first
123+
node.set['ip6address'] = ip6address_data["Address"]
124+
#node.set['ohai_time'] = ""
125+
node.set['recipes'] = ""
126+
127+
# Add other roles as seen fit
128+
node.set['roles'] = []
129+
["deployer-config-default", "network-config-default", "dns-config-default",
130+
"logging-config-default", "ntp-config-default", "nova-compute-kvm",
131+
"provisioner-base", "provisioner-config-default", "crowbar-#{node['fqdn']}"].each do |role_name|
132+
role = RoleObject.find_role_by_name "#{role_name}"
133+
node['roles'] += role
134+
end
135+
136+
node.set['run_list'] = ["role[#{node['roles']}]"]
137+
node.set['keys']['host']['host_dsa_public'] = ""
138+
node.set['keys']['host']['host_rsa_public'] = ""
139+
node.set['keys']['host']['host_ecdsa_public'] = ""
140+
node.set['virtualization']['system'] = "kvm"
141+
node.set['virtualization']['role'] = "guest"
142+
node.set['platform'] = "suse"
143+
node.set['platform_version'] = "12.1"
144+
node.set['dmi']['bios']['all_records'] = ""
145+
node.set['dmi']['bios']['vendor'] = ""
146+
node.set['dmi']['bios']['version'] = system_object["BiosVersion"]
147+
node.set['dmi']['bios']['release_date'] = ""
148+
node.set['dmi']['bios']['address'] = ""
149+
node.set['dmi']['bios']['runtime_size'] = ""
150+
node.set['dmi']['bios']['rom_size'] = ""
151+
node.set['dmi']['bios']['bios_revision'] = ""
152+
node.set['dmi']['system']['product_name'] = ""
153+
node.set['dmi']['system']['manufacturer'] = ""
154+
node.set['dmi']['system']['serial_number'] = "Not Specified"
155+
node.set['dmi']['system']['uuid'] = ""
156+
node.set['dmi']['system']['wake_up_type'] = "Power Switch"
157+
node.set['dmi']['system']['sku_number'] = "Not Specified"
158+
node.set['dmi']['system']['family'] = "Not Specified"
159+
node.set['dmi']['chassis']['serial_number'] = system_object['Chassis']['SerialNumber']
160+
node.set['dmi']['chassis']['all_records'] = ""
161+
node.set['dmi']['chassis']['manufacturer'] = ""
162+
node.set['dmi']['chassis']['all_records'] = ""
163+
node.set['dmi']['chassis']['boot_up_state'] = "Safe"
164+
node.set['dmi']['chassis']['power_supply_state'] = "Safe"
165+
node.set['block_device']['sda'] = ""
166+
node.set['memory']['swap'] = ""
167+
node.set['memory']['buffers'] = ""
168+
169+
system_object["Processors"].each do | processor |
170+
id = processor["Id"]
171+
node.set["cpu"]["#{id}"]['manufacturer'] = processor["Manufacturer"]
172+
node.set["cpu"]["#{id}"]["model"] = processor["Model"]
173+
node.set["cpu"]["#{id}"]["family"] = processor["ProcessorArchitecture"]
174+
node.set["cpu"]["#{id}"]["family"] = "x86_64" if processor["InstructionSet"] == "x86-64"
175+
node.set["cpu"]["#{id}"]["flags"] = processor["Capabilities"]
176+
end
177+
178+
node.set['filesystem']['sysfs'] = ""
179+
node.save
180+
end
181+
end
182+
183+
# usage of the controller APIs
184+
rsd_controller = IntelRSDController.new
185+
node_list = rsd_controller.get_rsd_nodes()
186+
first_node = node_list.first
187+
p "FIRST NODE: #{first_node}"
188+
# node_object = rsd_controller.get_crowbar_node_object(first_node["System_Id"])
189+
# p node_object
190+
Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
#
2+
# Copyright 2016, SUSE LINUX GmbH
3+
#
4+
# Licensed under the Apache License, Version 2.0 (the "License");
5+
# you may not use this file except in compliance with the License.
6+
# You may obtain a copy of the License at
7+
#
8+
# http://www.apache.org/licenses/LICENSE-2.0
9+
#
10+
# Unless required by applicable law or agreed to in writing, software
11+
# distributed under the License is distributed on an "AS IS" BASIS,
12+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
# See the License for the specific language governing permissions and
14+
# limitations under the License.
15+
#
16+
17+
require 'json'
18+
require 'uri'
19+
require 'net/http'
20+
require 'rest-client'
21+
22+
module RedfishHelper
23+
class RedfishClient
24+
attr_reader :logger
25+
26+
# Standard JSONRPC Error responses
27+
INVALID_JSON = -32700
28+
INVALID_REQUEST = -32600
29+
INVALID_PARAMS = -32602
30+
METHOD_NOT_FOUND = -32601
31+
INTERNAL_ERROR = -32603
32+
33+
# RedFish-specific constants
34+
REDFISH_VERSION = "redfish/v1/"
35+
36+
def initialize(host, port, insecure=true, client_cert=false)
37+
@service_uri = "https://#{host}:#{port}/#{REDFISH_VERSION}"
38+
@verify_ssl = OpenSSL::SSL::VERIFY_NONE if insecure
39+
@ssl_client_cert = false unless client_cert
40+
end
41+
42+
def handle_exception(json_rpc_error)
43+
logger.error(json_rpc_error[:message])
44+
end
45+
46+
def post_action(resource, action:None, params: None)
47+
uri = @service_uri + resource
48+
uri += "/Actions/#{action}" if action
49+
50+
begin
51+
response = RestClient::Request.execute(url: uri,
52+
method: :post,
53+
verify_ssl: @verify_ssl,
54+
ssl_client_cert: @ssl_client_cert)
55+
rescue
56+
handle_exception(response)
57+
end
58+
return JSON.parse(response)
59+
end
60+
61+
def get_resource(resource)
62+
uri = @service_uri + resource
63+
p "QUERYING RESOURCE: #{uri}"
64+
65+
begin
66+
response = RestClient::Request.execute(url: uri,
67+
method: :get,
68+
verify_ssl: @verify_ssl,
69+
ssl_client_cert: @ssl_client_cert)
70+
rescue
71+
handle_exception(response)
72+
end
73+
74+
return JSON.parse(response)
75+
end
76+
end
77+
end
78+
79+
# Usage Examples for this client library
80+
81+
# Create a client object
82+
redfish_client = RedfishHelper::RedfishClient.new("localhost", "8443")
83+
84+
# Check if the Redfish Service responds ( returns redfish/v1)
85+
api_resp = redfish_client.get_resource("")
86+
p api_resp
87+
88+
# Check one of the API responses
89+
systems = redfish_client.get_resource("Systems")
90+
p systems
91+
92+
sys_list = []
93+
94+
# Loop to run through all available systems and populate a node-object
95+
systems["Members"].each do |member|
96+
p "MEMBER DATA: #{member}"
97+
member_id= member["@odata.id"]
98+
p "MEMBER ID: #{member_id}"
99+
sys_id = member_id.split(/\//)[-1]
100+
p "SYSTEM ID: #{sys_id}"
101+
sys_data = Hash.new()
102+
sys_data["Systems"] = redfish_client.get_resource("Systems/#{sys_id}")
103+
p "SYSTEMS DATA: #{sys_data["Systems"]}"
104+
sys_data["Processors"] = redfish_client.get_resource("Systems/#{sys_id}/Processors/1")
105+
p "PROCESSORS DATA: #{sys_data["Processors"]}"
106+
sys_data["Memory"] = redfish_client.get_resource("Systems/#{sys_id}/Memory/1")
107+
p "MEMORY DATA: #{sys_data["Memory"]}"
108+
sys_data["MemoryChunks"] = redfish_client.get_resource("Systems/#{sys_id}/MemoryChunks/1")
109+
p "MEMORY CHUNKS DATA: #{sys_data["MemoryChunks"]}"
110+
sys_data["EthernetInterfaces"] = redfish_client.get_resource("Systems/#{sys_id}/EthernetInterfaces/1")
111+
p "ETHERNET DATA: #{sys_data["EthernetInterfaces"]}"
112+
sys_data["Adapters"] = redfish_client.get_resource("Systems/#{sys_id}/Adapters/1")
113+
p "ADAPTERS DATA: #{sys_data["Adapters"]}"
114+
sys_list.push(sys_data)
115+
end
116+
117+
p "NODE OBJECT From Redfish : #{sys_list}"
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
#
2+
# Copyright 2016, SUSE LINUX GmbH
3+
#
4+
# Licensed under the Apache License, Version 2.0 (the "License");
5+
# you may not use this file except in compliance with the License.
6+
# You may obtain a copy of the License at
7+
#
8+
# http://www.apache.org/licenses/LICENSE-2.0
9+
#
10+
# Unless required by applicable law or agreed to in writing, software
11+
# distributed under the License is distributed on an "AS IS" BASIS,
12+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
# See the License for the specific language governing permissions and
14+
# limitations under the License.
15+
#
16+
17+
class IntelRSD < ActiveResource::Base
18+
attr_accessor :url
19+
end

intelrsd.yml

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
#
2+
# Copyright 2016, SUSE LINUX GmbH
3+
#
4+
# Licensed under the Apache License, Version 2.0 (the "License");
5+
# you may not use this file except in compliance with the License.
6+
# You may obtain a copy of the License at
7+
#
8+
# http://www.apache.org/licenses/LICENSE-2.0
9+
#
10+
# Unless required by applicable law or agreed to in writing, software
11+
# distributed under the License is distributed on an "AS IS" BASIS,
12+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
# See the License for the specific language governing permissions and
14+
# limitations under the License.
15+
#
16+
17+
barclamp:
18+
name: intelrsd
19+
display: Intel RackScale Design
20+
description: Integration with Intel Rack Scale Design
21+
version: 1
22+
user_managed: false
23+
member:
24+
- crowbar
25+
26+
crowbar:
27+
layout: 1
28+
order: 112
29+
run_order: 112
30+
chef_order: 112
31+
proposal_schema_version: 3

0 commit comments

Comments
 (0)