blob: 9e6ea4fe5e4f84599a685d97c5514703c726ad30 [file] [log] [blame]
Jennifer Lee729dae72018-04-24 15:59:34 -07001/*
2// Copyright (c) 2018 Intel Corporation
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#pragma once
17
18#include "node.hpp"
Ed Tanous1abe55e2018-09-05 08:30:59 -070019
Jennifer Lee729dae72018-04-24 15:59:34 -070020#include <boost/container/flat_map.hpp>
Ed Tanousabf2add2019-01-22 16:40:12 -080021#include <variant>
Jennifer Lee729dae72018-04-24 15:59:34 -070022
Ed Tanous1abe55e2018-09-05 08:30:59 -070023namespace redfish
24{
Ed Tanous27826b52018-10-29 11:40:58 -070025
Jennifer Leeacb7cfb2018-06-07 16:08:15 -070026static std::unique_ptr<sdbusplus::bus::match::match> fwUpdateMatcher;
Jennifer Lee729dae72018-04-24 15:59:34 -070027
Ed Tanous1abe55e2018-09-05 08:30:59 -070028class UpdateService : public Node
29{
30 public:
31 UpdateService(CrowApp &app) : Node(app, "/redfish/v1/UpdateService/")
32 {
Ed Tanous1abe55e2018-09-05 08:30:59 -070033 entityPrivileges = {
34 {boost::beast::http::verb::get, {{"Login"}}},
35 {boost::beast::http::verb::head, {{"Login"}}},
36 {boost::beast::http::verb::patch, {{"ConfigureComponents"}}},
37 {boost::beast::http::verb::put, {{"ConfigureComponents"}}},
38 {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
39 {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
Jennifer Leeacb7cfb2018-06-07 16:08:15 -070040 }
Jennifer Leeacb7cfb2018-06-07 16:08:15 -070041
Ed Tanous1abe55e2018-09-05 08:30:59 -070042 private:
43 void doGet(crow::Response &res, const crow::Request &req,
44 const std::vector<std::string> &params) override
45 {
Ed Tanous0f74e642018-11-12 15:17:05 -080046 res.jsonValue["@odata.type"] = "#UpdateService.v1_2_0.UpdateService";
47 res.jsonValue["@odata.id"] = "/redfish/v1/UpdateService";
48 res.jsonValue["@odata.context"] =
49 "/redfish/v1/$metadata#UpdateService.UpdateService";
50 res.jsonValue["Id"] = "UpdateService";
51 res.jsonValue["Description"] = "Service for Software Update";
52 res.jsonValue["Name"] = "Update Service";
53 res.jsonValue["HttpPushUri"] = "/redfish/v1/UpdateService";
54 // UpdateService cannot be disabled
55 res.jsonValue["ServiceEnabled"] = true;
56 res.jsonValue["FirmwareInventory"] = {
57 {"@odata.id", "/redfish/v1/UpdateService/FirmwareInventory"}};
Jennifer Leeacb7cfb2018-06-07 16:08:15 -070058 res.end();
Ed Tanous1abe55e2018-09-05 08:30:59 -070059 }
60 static void activateImage(const std::string &objPath)
61 {
62 crow::connections::systemBus->async_method_call(
63 [objPath](const boost::system::error_code error_code) {
64 if (error_code)
65 {
66 BMCWEB_LOG_DEBUG << "error_code = " << error_code;
67 BMCWEB_LOG_DEBUG << "error msg = " << error_code.message();
68 }
69 },
70 "xyz.openbmc_project.Software.BMC.Updater", objPath,
71 "org.freedesktop.DBus.Properties", "Set",
72 "xyz.openbmc_project.Software.Activation", "RequestedActivation",
Ed Tanousabf2add2019-01-22 16:40:12 -080073 std::variant<std::string>(
Ed Tanous1abe55e2018-09-05 08:30:59 -070074 "xyz.openbmc_project.Software.Activation.RequestedActivations."
75 "Active"));
76 }
77 void doPost(crow::Response &res, const crow::Request &req,
78 const std::vector<std::string> &params) override
79 {
80 BMCWEB_LOG_DEBUG << "doPost...";
Jennifer Leeacb7cfb2018-06-07 16:08:15 -070081
Ed Tanous1abe55e2018-09-05 08:30:59 -070082 // Only allow one FW update at a time
83 if (fwUpdateMatcher != nullptr)
84 {
85 res.addHeader("Retry-After", "30");
Jason M. Billsf12894f2018-10-09 12:45:45 -070086 messages::serviceTemporarilyUnavailable(res, "3");
Ed Tanous1abe55e2018-09-05 08:30:59 -070087 res.end();
Jennifer Lee6c4eb9d2018-05-22 10:58:31 -070088 return;
Ed Tanous1abe55e2018-09-05 08:30:59 -070089 }
90 // Make this const static so it survives outside this method
91 static boost::asio::deadline_timer timeout(
92 *req.ioService, boost::posix_time::seconds(5));
Jennifer Lee6c4eb9d2018-05-22 10:58:31 -070093
Ed Tanous1abe55e2018-09-05 08:30:59 -070094 timeout.expires_from_now(boost::posix_time::seconds(5));
Jennifer Lee6c4eb9d2018-05-22 10:58:31 -070095
Ed Tanous1abe55e2018-09-05 08:30:59 -070096 timeout.async_wait([&res](const boost::system::error_code &ec) {
97 fwUpdateMatcher = nullptr;
98 if (ec == boost::asio::error::operation_aborted)
99 {
100 // expected, we were canceled before the timer completed.
101 return;
Jennifer Lee6c4eb9d2018-05-22 10:58:31 -0700102 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700103 BMCWEB_LOG_ERROR
104 << "Timed out waiting for firmware object being created";
105 BMCWEB_LOG_ERROR
106 << "FW image may has already been uploaded to server";
107 if (ec)
108 {
109 BMCWEB_LOG_ERROR << "Async_wait failed" << ec;
110 return;
111 }
112
Jason M. Billsf12894f2018-10-09 12:45:45 -0700113 redfish::messages::internalError(res);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700114 res.end();
115 });
116
117 auto callback = [&res](sdbusplus::message::message &m) {
118 BMCWEB_LOG_DEBUG << "Match fired";
Ed Tanous1abe55e2018-09-05 08:30:59 -0700119
120 if (m.is_method_error())
121 {
122 BMCWEB_LOG_DEBUG << "Dbus method error!!!";
123 res.end();
124 return;
125 }
126 std::vector<std::pair<
127 std::string,
Ed Tanousabf2add2019-01-22 16:40:12 -0800128 std::vector<std::pair<std::string, std::variant<std::string>>>>>
Ed Tanous3ae837c2018-08-07 14:41:19 -0700129 interfacesProperties;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700130
131 sdbusplus::message::object_path objPath;
132
Ed Tanous3ae837c2018-08-07 14:41:19 -0700133 m.read(objPath, interfacesProperties); // Read in the object path
134 // that was just created
Ed Tanous1abe55e2018-09-05 08:30:59 -0700135 // std::string str_objpath = objPath.str; // keep a copy for
136 // constructing response message
137 BMCWEB_LOG_DEBUG << "obj path = " << objPath.str; // str_objpath;
Ed Tanous3ae837c2018-08-07 14:41:19 -0700138 for (auto &interface : interfacesProperties)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700139 {
140 BMCWEB_LOG_DEBUG << "interface = " << interface.first;
141
142 if (interface.first ==
143 "xyz.openbmc_project.Software.Activation")
144 {
145 // cancel timer only when
146 // xyz.openbmc_project.Software.Activation interface is
147 // added
148 boost::system::error_code ec;
149 timeout.cancel(ec);
150 if (ec)
151 {
152 BMCWEB_LOG_ERROR << "error canceling timer " << ec;
153 }
154 UpdateService::activateImage(objPath.str); // str_objpath);
Jason M. Billsf12894f2018-10-09 12:45:45 -0700155 redfish::messages::success(res);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700156 BMCWEB_LOG_DEBUG << "ending response";
157 res.end();
158 fwUpdateMatcher = nullptr;
159 }
160 }
161 };
162
163 fwUpdateMatcher = std::make_unique<sdbusplus::bus::match::match>(
164 *crow::connections::systemBus,
165 "interface='org.freedesktop.DBus.ObjectManager',type='signal',"
166 "member='InterfacesAdded',path='/xyz/openbmc_project/software'",
167 callback);
168
169 std::string filepath(
170 "/tmp/images/" +
171 boost::uuids::to_string(boost::uuids::random_generator()()));
172 BMCWEB_LOG_DEBUG << "Writing file to " << filepath;
173 std::ofstream out(filepath, std::ofstream::out | std::ofstream::binary |
174 std::ofstream::trunc);
175 out << req.body;
176 out.close();
177 BMCWEB_LOG_DEBUG << "file upload complete!!";
178 }
Jennifer Lee729dae72018-04-24 15:59:34 -0700179};
Ed Tanousc711bf82018-07-30 16:31:33 -0700180
Ed Tanous1abe55e2018-09-05 08:30:59 -0700181class SoftwareInventoryCollection : public Node
182{
183 public:
184 template <typename CrowApp>
185 SoftwareInventoryCollection(CrowApp &app) :
186 Node(app, "/redfish/v1/UpdateService/FirmwareInventory/")
187 {
Ed Tanous1abe55e2018-09-05 08:30:59 -0700188 entityPrivileges = {
189 {boost::beast::http::verb::get, {{"Login"}}},
190 {boost::beast::http::verb::head, {{"Login"}}},
191 {boost::beast::http::verb::patch, {{"ConfigureComponents"}}},
192 {boost::beast::http::verb::put, {{"ConfigureComponents"}}},
193 {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
194 {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
Jennifer Lee729dae72018-04-24 15:59:34 -0700195 }
Jennifer Lee729dae72018-04-24 15:59:34 -0700196
Ed Tanous1abe55e2018-09-05 08:30:59 -0700197 private:
198 void doGet(crow::Response &res, const crow::Request &req,
199 const std::vector<std::string> &params) override
200 {
201 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
Ed Tanous0f74e642018-11-12 15:17:05 -0800202 res.jsonValue["@odata.type"] =
203 "#SoftwareInventoryCollection.SoftwareInventoryCollection";
204 res.jsonValue["@odata.id"] =
205 "/redfish/v1/UpdateService/FirmwareInventory";
206 res.jsonValue["@odata.context"] =
207 "/redfish/v1/"
208 "$metadata#SoftwareInventoryCollection.SoftwareInventoryCollection";
209 res.jsonValue["Name"] = "Software Inventory Collection";
Ed Tanousc711bf82018-07-30 16:31:33 -0700210
Ed Tanous1abe55e2018-09-05 08:30:59 -0700211 crow::connections::systemBus->async_method_call(
212 [asyncResp](
213 const boost::system::error_code ec,
214 const std::vector<std::pair<
215 std::string, std::vector<std::pair<
216 std::string, std::vector<std::string>>>>>
217 &subtree) {
218 if (ec)
219 {
Jason M. Billsf12894f2018-10-09 12:45:45 -0700220 messages::internalError(asyncResp->res);
Ed Tanousc711bf82018-07-30 16:31:33 -0700221 return;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700222 }
223 asyncResp->res.jsonValue["Members"] = nlohmann::json::array();
224 asyncResp->res.jsonValue["Members@odata.count"] = 0;
Jennifer Lee6c4eb9d2018-05-22 10:58:31 -0700225
Ed Tanous1abe55e2018-09-05 08:30:59 -0700226 for (auto &obj : subtree)
227 {
228 const std::vector<
229 std::pair<std::string, std::vector<std::string>>>
230 &connections = obj.second;
231
Jennifer Leef4b65ab2018-09-18 12:00:13 -0700232 // if can't parse fw id then return
Ed Tanous27826b52018-10-29 11:40:58 -0700233 std::size_t idPos;
234 if ((idPos = obj.first.rfind("/")) == std::string::npos)
Jennifer Leef4b65ab2018-09-18 12:00:13 -0700235 {
Jason M. Billsf12894f2018-10-09 12:45:45 -0700236 messages::internalError(asyncResp->res);
Jennifer Leef4b65ab2018-09-18 12:00:13 -0700237 BMCWEB_LOG_DEBUG << "Can't parse firmware ID!!";
238 return;
239 }
Jennifer Leef4b65ab2018-09-18 12:00:13 -0700240 std::string swId = obj.first.substr(idPos + 1);
241
Ed Tanous1abe55e2018-09-05 08:30:59 -0700242 for (auto &conn : connections)
243 {
244 const std::string &connectionName = conn.first;
245 BMCWEB_LOG_DEBUG << "connectionName = "
246 << connectionName;
247 BMCWEB_LOG_DEBUG << "obj.first = " << obj.first;
248
249 crow::connections::systemBus->async_method_call(
Jennifer Leef4b65ab2018-09-18 12:00:13 -0700250 [asyncResp,
251 swId](const boost::system::error_code error_code,
252 const VariantType &activation) {
Ed Tanous1abe55e2018-09-05 08:30:59 -0700253 BMCWEB_LOG_DEBUG
254 << "safe returned in lambda function";
255 if (error_code)
256 {
Jason M. Billsf12894f2018-10-09 12:45:45 -0700257 messages::internalError(asyncResp->res);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700258 return;
259 }
260
Jennifer Leef4b65ab2018-09-18 12:00:13 -0700261 const std::string *swActivationStatus =
Ed Tanousabf2add2019-01-22 16:40:12 -0800262 std::get_if<std::string>(&activation);
Jennifer Leef4b65ab2018-09-18 12:00:13 -0700263 if (swActivationStatus == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700264 {
Jason M. Billsf12894f2018-10-09 12:45:45 -0700265 messages::internalError(asyncResp->res);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700266 return;
267 }
Jennifer Leef4b65ab2018-09-18 12:00:13 -0700268 if (swActivationStatus != nullptr &&
269 *swActivationStatus !=
270 "xyz.openbmc_project.Software."
271 "Activation."
272 "Activations.Active")
Ed Tanous1abe55e2018-09-05 08:30:59 -0700273 {
Jennifer Leef4b65ab2018-09-18 12:00:13 -0700274 // The activation status of this software is
275 // not currently active, so does not need to
276 // be listed in the response
Ed Tanous1abe55e2018-09-05 08:30:59 -0700277 return;
278 }
279 nlohmann::json &members =
280 asyncResp->res.jsonValue["Members"];
281 members.push_back(
Jennifer Leef4b65ab2018-09-18 12:00:13 -0700282 {{"@odata.id", "/redfish/v1/UpdateService/"
283 "FirmwareInventory/" +
284 swId}});
Ed Tanous1abe55e2018-09-05 08:30:59 -0700285 asyncResp->res
286 .jsonValue["Members@odata.count"] =
287 members.size();
288 },
289 connectionName, obj.first,
290 "org.freedesktop.DBus.Properties", "Get",
291 "xyz.openbmc_project.Software.Activation",
292 "Activation");
Ed Tanousc711bf82018-07-30 16:31:33 -0700293 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700294 }
295 },
296 "xyz.openbmc_project.ObjectMapper",
297 "/xyz/openbmc_project/object_mapper",
298 "xyz.openbmc_project.ObjectMapper", "GetSubTree",
299 "/xyz/openbmc_project/software", int32_t(1),
300 std::array<const char *, 1>{
301 "xyz.openbmc_project.Software.Version"});
302 }
Jennifer Lee729dae72018-04-24 15:59:34 -0700303};
304
Ed Tanous1abe55e2018-09-05 08:30:59 -0700305class SoftwareInventory : public Node
306{
307 public:
308 template <typename CrowApp>
309 SoftwareInventory(CrowApp &app) :
310 Node(app, "/redfish/v1/UpdateService/FirmwareInventory/<str>/",
311 std::string())
312 {
Ed Tanous1abe55e2018-09-05 08:30:59 -0700313 entityPrivileges = {
314 {boost::beast::http::verb::get, {{"Login"}}},
315 {boost::beast::http::verb::head, {{"Login"}}},
316 {boost::beast::http::verb::patch, {{"ConfigureComponents"}}},
317 {boost::beast::http::verb::put, {{"ConfigureComponents"}}},
318 {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
319 {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
320 }
321
322 private:
323 void doGet(crow::Response &res, const crow::Request &req,
324 const std::vector<std::string> &params) override
325 {
326 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
Ed Tanous0f74e642018-11-12 15:17:05 -0800327 res.jsonValue["@odata.type"] =
328 "#SoftwareInventory.v1_1_0.SoftwareInventory";
329 res.jsonValue["@odata.context"] =
330 "/redfish/v1/$metadata#SoftwareInventory.SoftwareInventory";
331 res.jsonValue["Name"] = "Software Inventory";
332 res.jsonValue["Updateable"] = false;
333 res.jsonValue["Status"]["Health"] = "OK";
334 res.jsonValue["Status"]["HealthRollup"] = "OK";
335 res.jsonValue["Status"]["State"] = "Enabled";
Ed Tanous1abe55e2018-09-05 08:30:59 -0700336
337 if (params.size() != 1)
338 {
Jason M. Billsf12894f2018-10-09 12:45:45 -0700339 messages::internalError(res);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700340 res.end();
341 return;
342 }
343
Ed Tanous3ae837c2018-08-07 14:41:19 -0700344 std::shared_ptr<std::string> swId =
Ed Tanous1abe55e2018-09-05 08:30:59 -0700345 std::make_shared<std::string>(params[0]);
346
347 res.jsonValue["@odata.id"] =
Ed Tanous3ae837c2018-08-07 14:41:19 -0700348 "/redfish/v1/UpdateService/FirmwareInventory/" + *swId;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700349
350 crow::connections::systemBus->async_method_call(
Ed Tanous3ae837c2018-08-07 14:41:19 -0700351 [asyncResp, swId](
Ed Tanous1abe55e2018-09-05 08:30:59 -0700352 const boost::system::error_code ec,
353 const std::vector<std::pair<
354 std::string, std::vector<std::pair<
355 std::string, std::vector<std::string>>>>>
356 &subtree) {
357 BMCWEB_LOG_DEBUG << "doGet callback...";
358 if (ec)
359 {
Jason M. Billsf12894f2018-10-09 12:45:45 -0700360 messages::internalError(asyncResp->res);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700361 return;
362 }
363
364 for (const std::pair<
365 std::string,
366 std::vector<
367 std::pair<std::string, std::vector<std::string>>>>
368 &obj : subtree)
369 {
Ed Tanous3ae837c2018-08-07 14:41:19 -0700370 if (boost::ends_with(obj.first, *swId) != true)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700371 {
372 continue;
373 }
374
Jennifer Leef4b65ab2018-09-18 12:00:13 -0700375 if (obj.second.size() < 1)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700376 {
377 continue;
378 }
379
380 crow::connections::systemBus->async_method_call(
381 [asyncResp,
Ed Tanous3ae837c2018-08-07 14:41:19 -0700382 swId](const boost::system::error_code error_code,
383 const boost::container::flat_map<
384 std::string, VariantType> &propertiesList) {
Ed Tanous1abe55e2018-09-05 08:30:59 -0700385 if (error_code)
386 {
Jason M. Billsf12894f2018-10-09 12:45:45 -0700387 messages::internalError(asyncResp->res);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700388 return;
389 }
390 boost::container::flat_map<
391 std::string, VariantType>::const_iterator it =
392 propertiesList.find("Purpose");
393 if (it == propertiesList.end())
394 {
395 BMCWEB_LOG_DEBUG
396 << "Can't find property \"Purpose\"!";
Jason M. Billsf12894f2018-10-09 12:45:45 -0700397 messages::propertyMissing(asyncResp->res,
398 "Purpose");
Ed Tanous1abe55e2018-09-05 08:30:59 -0700399 return;
400 }
Ed Tanous3ae837c2018-08-07 14:41:19 -0700401 const std::string *swInvPurpose =
Ed Tanousabf2add2019-01-22 16:40:12 -0800402 std::get_if<std::string>(&it->second);
Ed Tanous3ae837c2018-08-07 14:41:19 -0700403 if (swInvPurpose == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700404 {
405 BMCWEB_LOG_DEBUG
406 << "wrong types for property\"Purpose\"!";
Jason M. Billsf12894f2018-10-09 12:45:45 -0700407 messages::propertyValueTypeError(asyncResp->res,
408 "", "Purpose");
Ed Tanous1abe55e2018-09-05 08:30:59 -0700409 return;
410 }
411
Ed Tanous3ae837c2018-08-07 14:41:19 -0700412 BMCWEB_LOG_DEBUG << "swInvPurpose = "
413 << *swInvPurpose;
Jennifer Leef4b65ab2018-09-18 12:00:13 -0700414 it = propertiesList.find("Version");
415 if (it == propertiesList.end())
Ed Tanous1abe55e2018-09-05 08:30:59 -0700416 {
Jennifer Leef4b65ab2018-09-18 12:00:13 -0700417 BMCWEB_LOG_DEBUG
418 << "Can't find property \"Version\"!";
Jason M. Billsf12894f2018-10-09 12:45:45 -0700419 messages::propertyMissing(asyncResp->res,
420 "Version");
Jennifer Leef4b65ab2018-09-18 12:00:13 -0700421 return;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700422 }
Jennifer Leef4b65ab2018-09-18 12:00:13 -0700423
424 BMCWEB_LOG_DEBUG << "Version found!";
425
426 const std::string *version =
Ed Tanousabf2add2019-01-22 16:40:12 -0800427 std::get_if<std::string>(&it->second);
Jennifer Leef4b65ab2018-09-18 12:00:13 -0700428
429 if (version == nullptr)
430 {
431 BMCWEB_LOG_DEBUG
432 << "Can't find property \"Version\"!";
Jason M. Billsf12894f2018-10-09 12:45:45 -0700433
434 messages::propertyValueTypeError(asyncResp->res,
435 "", "Version");
Jennifer Leef4b65ab2018-09-18 12:00:13 -0700436 return;
437 }
438 asyncResp->res.jsonValue["Version"] = *version;
439 asyncResp->res.jsonValue["Id"] = *swId;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700440 },
441 obj.second[0].first, obj.first,
442 "org.freedesktop.DBus.Properties", "GetAll",
443 "xyz.openbmc_project.Software.Version");
444 }
445 },
446 "xyz.openbmc_project.ObjectMapper",
447 "/xyz/openbmc_project/object_mapper",
448 "xyz.openbmc_project.ObjectMapper", "GetSubTree",
449 "/xyz/openbmc_project/software", int32_t(1),
450 std::array<const char *, 1>{
451 "xyz.openbmc_project.Software.Version"});
452 }
453};
454
455} // namespace redfish