Age | Commit message (Collapse) | Author | Files | Lines |
|
Change MemoryMedia, Slot and Socket memory attributes to be byte, in
line with Phosphor-dbus-interfaces.
Tested:
1. Redfish validator - passed for this new change
2. Populated MiemoryMedia, Slot and Socket values as expected.
GET https://<BMC_IP>/redfish/v1/Systems/system/Memory/dimm0
Signed-off-by: poram srinivasa rao <poramx.srinivasa.rao@intel.com>
Signed-off-by: Jayaprakash Mutyala <mutyalax.jayaprakash@intel.com>
Signed-off-by: Ed Tanous <edtanous@google.com>
Change-Id: Ib4894aca135af79d01ff0fda0ae8f3f1d03ea278
|
|
This commit addresses the Redfish validator failure related to the URI
mismatch in PCIeFunction. The error reported that the URI
"/redfish/v1/Systems/system/PCIeDevices/pcie_card1/PCIeFunctions/" does
not match the required URI in the PCIeFunction schema.
Commit that introduced the Redfish validator failure:
https://gerrit.openbmc.org/c/openbmc/bmcweb/+/63853
Tested: validator passed
Change-Id: Ie96b7461ad64f9b1c6392e4905276a4fe7799781
Signed-off-by: Lakshmi Yadlapati <lakshmiy@us.ibm.com>
|
|
This adds HTTP response headers Referrer-Policy and Permissions-Policy per
OWASP guidelines, with some appropriate values for BMCWeb.
https://owasp.org/www-project-secure-headers/
Policies are given for all standardized feature. Most features are disabled
except for the following which the web application uses: usb=(self).
Tested: Yes
Via curl, confirmed headers are present.
On selected browsers, opened browser tools and confirmed browsers didn't
complain about the new headers. Browsers checked were:
- Firefox 111.0.1 (64-bit)
- Safari Version 16.4 (18615.1.26.11.23)
Did not test access to features secured by the Permissions-Policy.
Did not test if the web application features still work.
Change-Id: I65f89d2959b0b1338c20d7222229fbdc1d720834
Signed-off-by: Joseph Reynolds <joseph-reynolds@charter.net>
|
|
This variable was poorly named. At one point it represented mozilla
modern cipher suites, but it has been long since changed to mozilla
intermediate. Name the variable appropriately.
While we're here, also change the type to const char*, such that we're
not allocating the string for every connection.
Change-Id: I0faae73448d953c173c3d3b9e4916b41b2a2497a
Signed-off-by: Ed Tanous <edtanous@google.com>
|
|
Mozilla intermediate 5.7 was released last month[1]
The last release to these was 3 years ago, so we haven't really had to
update much. Update cipher suites to match new list for mozilla
intermediate.
Note, the variable is called "mozilla modern" but it hasn't tracked the
modern recommendations for some time.
Tested:
testssl.sh, from the master branch (864877df)
Returns a passing result, showing no change in supported products, and
the cipher suites correctly applied.
Redfish service validator shows no change in result.
[1] https://ssl-config.mozilla.org/guidelines/5.7.json
[2] https://github.com/mozilla/ssl-config-generator/tree/master/src/static/guidelines
Tested: WIP
Change-Id: Ie9ccb7757ae527fa3ac129f781ae32657c7dfdd9
Signed-off-by: Ed Tanous <edtanous@google.com>
|
|
There's two instances of log service that the method from
fillMessageArgs was copy/pasted to. This commit moves them to using the
common, unit tested function.
Tested:
- Verified the GET on Event logs and works as expected.
curl -k -u $USER:$PASSWORD --noproxy "*"
https://${BMCIP}/redfish/v1/Systems/system/LogServices/EventLog/Entries -X GET
- Verified the PostCode fetching and works as expected.
curl -k -u $USER:$PASSWORD --noproxy "*"
https://${BMCIP}/redfish/v1/Systems/system/LogServices/PostCodes/Entries -X GET
Signed-off-by: Ed Tanous <edtanous@google.com>
Change-Id: I24eb13b83eff41e7dfd98ea9ecc160d578d2faa6
|
|
Since the getManagedObjects method has been implemented in
dbus_utility and this commit is to integrate all the places where the
GetManagedObjects method is obtained, and use the method in
dbus_utility uniformly.
Signed-off-by: George Liu <liuxiwei@inspur.com>
Change-Id: Ic13f2bef7b30f805cd3444a75d7df17b031f2eb0
|
|
Change-Id: Ib5fb6dcfaf63520cbc07ca909e0806480440296a
Signed-off-by: Ed Tanous <edtanous@google.com>
|
|
This reverts commit e628df8658c57f6943b6d3612e1077618e5a168a.
This appears to cause problems with non-cookie login of the console
websocket. This appears to be a gap in both our testing, and things
that we have scripting to do, but clearly it's a change in behavior, so
if we want to change the behavior, we should do it intentionally, and
clearly, ideally with a path to make clients work, or an explicit
documentation that the webui is the only supported client.
Change-Id: I334257e1355a5b8431cb7ecfe58ef8a942f4981c
Signed-off-by: Ed Tanous <edtanous@google.com>
|
|
This commit is to add Health information according to the Redfish
PCIeDevice schema.
ref: https://redfish.dmtf.org/schemas/v1/PCIeDevice.v1_9_0.json
Code that updates the OperationalStatus for all the inventory
https://github.com/openbmc/openpower-vpd-parser/blob/ \
3fb026386546cfd288ab4f86156c9aa0ffa145d6/ibm_vpd_app.cpp#L620
Tested: Validator passed
'''
curl -k https://$bmc/redfish/v1/Systems/system/PCIeDevices/pcie_card8
{
"@odata.id": "/redfish/v1/Systems/system/PCIeDevices/pcie_card8",
"@odata.type": "#PCIeDevice.v1_9_0.PCIeDevice",
"Id": "pcie_card8",
"Manufacturer": "",
"Model": "6B87",
"Name": "PCIe Device",
"PCIeFunctions": {
"@odata.id": "/redfish/v1/Systems/system/PCIeDevices/pcie_card8/PCIeFunctions"
},
"PCIeInterface": {
"LanesInUse": -1
},
"PartNumber": "03FL194",
"SerialNumber": "Y131UF09S00J",
"Slot": {
"Location": {
"PartLocation": {
"ServiceLabel": "U78DB.ND0.WZS0018-P0-C8"
}
}
},
"SparePartNumber": "03FL195",
"Status": {
"Health": "OK",
"State": "Enabled"
}
}
'''
Change-Id: I53026792d0c223c10065c58aef9f3b9dc04a24ed
Signed-off-by: Lakshmi Yadlapati <lakshmiy@us.ibm.com>
|
|
Move redfishPcieGenerationFromDbus, called from both pcie.hpp and
pcie_slots.hpp, and busSlotTypeToRf functions, called from
pcie_slots.hpp, to a common PCIe utility file.
In the future commit, when integrating PCIeSlot with PCIeDevice, we will
call the busSlotTypeToRf function from pcie.hpp, so having it in the
common utility file will make it readily available.
Tested: build successful, no additional testing needed.
Change-Id: I6286bd5547ddafa6eac4f224ac56f6d790a44c7a
Signed-off-by: Lakshmi Yadlapati <lakshmiy@us.ibm.com>
|
|
This commit is to add Location/PartLocation/ServiceLabel information
according to the Redfish PowerSupply schema.
If the `xyz.openbmc_project.Inventory.Decorator.LocationCode`
interface does not exist, the ServiceLabel information property is
not displayed.
ref: http://redfish.dmtf.org/schemas/v1/PowerSupply.v1_5_0.json
Tested: Validator passes
curl -k -H "X-Auth-Token: $token" -X GET
https://${bmc}/redfish/v1/Chassis/chassis/PowerSubsystem/
PowerSupplies/powersupply0
{
"@odata.id": "/redfish/v1/Chassis/chassis/PowerSubsystem/
PowerSupplies/powersupply0",
"@odata.type": "#PowerSupply.v1_5_0.PowerSupply",
"FirmwareVersion": "383239323732",
"Id": "powersupply0",
"Location": {
"PartLocation": {
"ServiceLabel": "U78DA.ND0.WZS00F6-E0"
}
},
"Manufacturer": "",
"Model": "51E9",
"Name": "powersupply0",
"PartNumber": "03KP466",
"SerialNumber": "YL10KY26E073",
"SparePartNumber": "03FP378",
"Status": {
"Health": "OK"
}
}
Signed-off-by: George Liu <liuxiwei@inspur.com>
Change-Id: Icd0c21719bb01f481a8cd4c56bdb1a9ee047b047
|
|
5b224921d765a93c5f93a6012109a9a748ef7cd4
and
a1e0871d2425b3f42d0e5893d548593e5ed576b
Added code to handle links looked correct in review, but the
deduplication of the HEAD methods now causes the return code of
setUpRedfishRoute to be ignored. This means that query parameter
or other header failures don't stop the request, which is bad.
Tested:
GET /redfish/v1 (ie ServiceRoot) returns the correct header.
Signed-off-by: Ed Tanous <edtanous@google.com>
Change-Id: Iec01051221550747a7b99da5eb7713e18394d530
|
|
This commit is to add firmware version information according to the
Redfish PowerSupply schema.
If the `xyz.openbmc_project.Software.Version` interface does not
exist, the firmware version information property is not displayed.
ref: http://redfish.dmtf.org/schemas/v1/PowerSupply.v1_5_0.json
Tested: Validator passes
curl -k -H "X-Auth-Token: $token" -X GET
https://${bmc}/redfish/v1/Chassis/chassis/PowerSubsystem/
PowerSupplies/powersupply0
{
"@odata.id": "/redfish/v1/Chassis/chassis/PowerSubsystem/
PowerSupplies/powersupply0",
"@odata.type": "#PowerSupply.v1_5_0.PowerSupply",
"FirmwareVersion": "383239323732",
"Id": "powersupply0",
"Manufacturer": "",
"Model": "51E9",
"Name": "powersupply0",
"PartNumber": "03KP466",
"SerialNumber": "YL10KY26E073",
"SparePartNumber": "03FP378",
"Status": {
"Health": "OK"
}
}
Signed-off-by: George Liu <liuxiwei@inspur.com>
Change-Id: I3ced467faf49d08c46a8b9bb2aa722f35353c811
|
|
This commit is to add asset information according to the Redfish
PowerSupply schema.
If the `xyz.openbmc_project.Inventory.Decorator.Asset` interface does
not exist, the asset information property is not displayed.
ref: http://redfish.dmtf.org/schemas/v1/PowerSupply.v1_5_0.json
Tested: Validator passes
curl -k -H "X-Auth-Token: $token" -X GET
https://${bmc}/redfish/v1/Chassis/chassis/PowerSubsystem/
PowerSupplies/powersupply0
{
"@odata.id": "/redfish/v1/Chassis/chassis/PowerSubsystem/
PowerSupplies/powersupply0",
"@odata.type": "#PowerSupply.v1_5_0.PowerSupply",
"Id": "powersupply0",
"Manufacturer": "",
"Model": "51E9",
"Name": "powersupply0",
"PartNumber": "03KP466",
"SerialNumber": "YL10KY26E073",
"SparePartNumber": "03FP378",
"Status": {
"Health": "OK"
}
}
Signed-off-by: George Liu <liuxiwei@inspur.com>
Change-Id: I83f1a9375f83e3470089cb2b5207db9665cc69df
|
|
This header didn't include a pragma once. Fix it.
Tested: Code compiles
Change-Id: I8bd4f9d870ec9b7dc1687e8de1c8a61d93140c7e
Signed-off-by: Ed Tanous <edtanous@google.com>
|
|
https://github.com/openbmc/bmcweb/commit/13451e3913c29bb380ef6778f11749d337ddd442
added this. I think just a mistake.
Noticed this while reviewing
https://gerrit.openbmc.org/c/openbmc/bmcweb/+/40978/23/config/meson.build#24
Tested: None. Confirmed via find these lines are the exact same.
Change-Id: I40ec3e7f27ef0cbdc1afd6ba2daad637f1041d11
Signed-off-by: Gunnar Mills <gmills@us.ibm.com>
|
|
The previous patches modified these fields one at a time, for the cases
where we had code calling these in odd ways. This commit goes and
effects the same change on the rest of the message handlers where it
makes sense.
Any input to a message function that mentions a "value" replaces the
value with a nlohmann::json object, to make calling this code easier.
The one exception is StringValueTooLong, which by definition, requires
the input value to be a string, so that is left as string_view
intentionally.
Tested: Dead unused code. Code compiles.
Signed-off-by: Ed Tanous <edtanous@google.com>
Change-Id: I00b9804b2aadf5584032be91a0ee53b9a94a7430
|
|
Similar to the previous patches, make propertyValueIncorrect accept a
nlohmann::json object as input. This removes the need for the dump()
call, which oddly enough, in our one usage, was actually incorrect, and
could cause bmcweb to throw an exception in parsing in theory.
Tested: Only used in one error condition. Code compiles.
Signed-off-by: Ed Tanous <edtanous@google.com>
Change-Id: Ie35d61101c2db88b16d42d71e66bceef540e8429
|
|
Similar to other patches, make propertyValueFormatError accept a
nlohmann::json object, which removes a lot of the unsafe dump code that
we have littered about.
Tested: No easy to replicate error. Code is identical to previous
patchsets. Inspection and code compilation only.
Signed-off-by: Ed Tanous <edtanous@google.com>
Change-Id: Ic9d0f196b6e198073189f744b738db7ffa2f1b74
|
|
Similar to the prior patchset in this series, propertyValueTypeError can
be moved to safer constructs. This ensures that we are minimizing how
many places we are calling dump() from, and allows us to reduce the
amount of code written for error handling.
Tested:
PATCH /redfish/v1/SessionService {"SessionTimeout": "foo"}
Returns PropertyValueTypeError in the same behavior as prior to this
patch.
Signed-off-by: Ed Tanous <edtanous@google.com>
Change-Id: Iddff4b787f35c49bf923663d61bba156687f358c
|
|
The error codes for this function accept a string_view, which has caused
a number of cases of users of this function to call dump() to_string()
and all manner of other conversions. Considering that dump() is
something that's difficult to call correctly, and overly wordy, it would
be ideal if the message code just handled that for us.
Therefore, this commit changes the prototype to include a nlohmann::json
object as an argument instead of string_view, then audits the codebase
for all uses, and moves them to a more normalized usage, which allows
the calling code to call "dump" for them.
Tested: PATCH /redfish/v1/SessionService {"SessionTimeout": 1}
Returns the PropertyValueNotInList error as it did before.
Signed-off-by: Ed Tanous <edtanous@google.com>
Change-Id: If62909072db1f067ad1f8aa590bb716c84181219
|
|
This code was added into the registries namespace, from event service
manager. It is operating on registries, it belongs in the registry
header.
Tested: Redfish service validator passes.
Signed-off-by: Ed Tanous <edtanous@google.com>
Change-Id: I89e830dde185178b1d2e52b18a1a0a2baa4e0fb3
|
|
This commit is to add PowerSupply State/Health status according to the
Redfish PowerSupply schema.
If the `xyz.openbmc_project.Inventory.Item`
interface does not exist, the state status property is set to
default "Present".
If the `xyz.openbmc_project.State.Decorator.OperationalStatus`
interface does not exist, the health status property is set to
default "OK".
ref: http://redfish.dmtf.org/schemas/v1/PowerSupply.v1_5_0.json
Code that updates the OperationalStatus for all the inventory
https://github.com/openbmc/openpower-vpd-parser/blob/ \
3fb026386546cfd288ab4f86156c9aa0ffa145d6/ibm_vpd_app.cpp#L620
Tested: Validator passes
curl -k -H "X-Auth-Token: $token" -X GET
https://${bmc}/redfish/v1/Chassis/chassis/PowerSubsystem/
PowerSupplies/powersupply0
{
"@odata.id": "/redfish/v1/Chassis/chassis/PowerSubsystem/
PowerSupplies/powersupply0",
"@odata.type": "#PowerSupply.v1_5_0.PowerSupply",
"Id": "powersupply0",
"Name": "powersupply0",
"Status": {
"Health": "OK"
"State": "Enabled"
}
}
Signed-off-by: George Liu <liuxiwei@inspur.com>
Change-Id: I121b665a4e605024644cc7c9392f88a71703481e
Signed-off-by: Lakshmi Yadlapati <lakshmiy@us.ibm.com>
|
|
Remove passing ec by value for getProperty.
Change-Id: I9070ca7035fead14faa236eb1b17937ffe13045b
Signed-off-by: Willy Tu <wltu@google.com>
|
|
The content type check is required in the update service. This may be a
security issue.
The correct content types to update service are multipart/form-data and
application/octet-stream. The multipart content type was missing from
the contentTypes array which is added in this drop.
Added couple of test cases.
Tested:
1) Make sure that update service content type
application/octet-stream are working correctly.
$ curl -k -H 'X-Auth-Token: THIbp1G0DiNVj3JrCZMf' -H 'Content-Type: \
application/octet-stream' -X POST \
-T ./obmc-phosphor-image-everest.ext4.mmc.tar \
https://${bmc}/redfish/v1/UpdateService/update
{
"@odata.id": "/redfish/v1/TaskService/Tasks/0",
"@odata.type": "#Task.v1_4_3.Task",
"Id": "0",
"TaskState": "Running",
"TaskStatus": "OK"
2) Make sure that update service content type
multipart/form-data are working correctly.
$ curl -k -H 'X-Auth-Token: Vc7KBgM6z3uMs1G7uVqu' \
-H Content-Type:multipart/form-data \
-F 'UpdateParameters={"Targets":["/redfish/v1/Managers/bmc"], \
"@Redfish.OperationApplyTime":"Immediate"};type=application/json'\
-F 'UpdateFile=@obmc-phosphor-image-p10bmc.ext4.mmc.tar; \
type=application/octet-stream' \
https://${bmc}/redfish/v1/UpdateService/update
{
"@Message.ExtendedInfo": [
{
"@odata.type": "#Message.v1_1_1.Message",
"Message": "The request completed successfully.",
"MessageArgs": [],
"MessageId": "Base.1.16.0.Success",
"MessageSeverity": "OK",
"Resolution": "None"
}
],
"@odata.id": "/redfish/v1/TaskService/Tasks/0",
"@odata.type": "#Task.v1_4_3.Task",
"Id": "0",
"TaskState": "Running",
"TaskStatus": "OK"
3) Make sure that command fails when content type is missing.
$ curl -k -H 'X-Auth-Token: vH3B88sh323sfy0YG8eN' -H Content-Type: \
-X POST -T ./obmc-phosphor-image-everest.ext4.mmc.tar \
https://${bmc}/redfish/v1/UpdateService/update
Bad Request
[INFO "http_connection.hpp":201] Request: 0x177f920 HTTP/1.1 POST \
/redfish/v1/UpdateService/update ::ffff:x.x.xx.xxx
|
[DEBUG "update_service.hpp":687] doPost: contentType=
[DEBUG "update_service.hpp":692] Bad content type specified:
4) Make sure that command fails when wrong cotent type specified.
$ curl -k -H 'X-Auth-Token: OQzODMaR0G29AjpD2YmT' -H 'Content-Type: \
application/octet-json' -X POST \
-T ./obmc-phosphor-image-everest.ext4.mmc.tar \
https://${bmc}/redfish/v1/UpdateService/update
Bad Request
[INFO "http_connection.hpp":201] Request: 0x17a69d0 HTTP/1.1 POST \
/redfish/v1/UpdateService/update ::ffff:x.x.xx.xxx
|
[DEBUG "update_service.hpp":687] doPost:
contentType=application/octet-json
[DEBUG "update_service.hpp":720] Bad content type specified:
application/octet-json
5) Make sure that command fails when header is not specified.
$ curl -k -H 'X-Auth-Token: Z1AEXg075qGKi0xISu6o' -X POST \
-T ./obmc-phosphor-image-everest.ext4.mmc.tar \
https://${bmc}/redfish/v1/UpdateService/update
Bad Request
[INFO "http_connection.hpp":201] Request: 0x17a69d0 HTTP/1.1 \
POST /redfish/v1/UpdateService/update ::ffff:x.x.xx.xxx
|
[DEBUG "update_service.hpp":687] doPost: contentType=
[DEBUG "update_service.hpp":692] Bad content type specified:
Change-Id: I48b709b6219debfed9ff60dd46ab87652e5a1fe5
Signed-off-by: Ninad Palsule <ninad@linux.ibm.com>
|
|
Updated Storage resource to `#Storage.v1_9_1.Storage` to support the
change.
Follow the Swordfish spec to setup the Storage relationship[1].
There will now be two Storage Collection `/redfish/v1/Stroage` and
`/redfish/v1/Systems/system/Storage`. The storage in `/Storage` will be
treated as a subsystem and only link to the `/Systems/system/Storage`
under `Links/StorageServices` resource.
The `/Storage` won't contain Drives or StorageControllers.
Tested:
Passed Redfish Validator for related resources.
```
*** /redfish/v1/Storage/storage_1
INFO - Type (Storage.v1_7_1.Storage), GET SUCCESS (time: 0)
WARNING - StorageControllers: The given property is deprecated: This property has been deprecated in favor of Controllers to allow for storage controllers to be represented as their own resources.
INFO - Attempt 1 of /redfish/v1/Chassis/chassis0/Drives/drive0
INFO - Response Time for GET to /redfish/v1/Chassis/chassis_0/Drives/drive_0: 0.07591272401623428 seconds.
INFO - PASS
INFO -
```
Chassis
```
wget -qO- http://localhost:80/redfish/v1/Chassis/chassis0
{
"@odata.id": "/redfish/v1/Chassis/chassis0",
"@odata.type": "#Chassis.v1_14_0.Chassis",
"Id": "chassis0",
"Links": {
"Storage": [
{
"@odata.id": "/redfish/v1/Systems/system/Storage/storage0"
}
],
"Storage@odata.count": 1
},
"Name": "chassis0",
}}
```
Storage Collection
```
wget -qO- http://localhost:80/redfish/v1/Storage
{
"@odata.id": "/redfish/v1/Storage",
"@odata.type": "#StorageCollection.StorageCollection",
"Members": [
{
"@odata.id": "/redfish/v1/Storage/storage0"
}
],
"Members@odata.count": 1,
"Name": "Storage Collection"
}
wget -qO- http://localhost:80/redfish/v1/Systems/system/Storage
{
"@odata.id": "/redfish/v1/Systems/system/Storage",
"@odata.type": "#StorageCollection.StorageCollection",
"Members": [
{
"@odata.id": "/redfish/v1/Systems/system/Storage/storage0"
}
],
"Members@odata.count": 1,
"Name": "Storage Collection"
}
```
Storage
```
wget -qO- http://localhost:80/redfish/v1/Storage/storage0
{
"@odata.id": "/redfish/v1/Storage/storage0",
"@odata.type": "#Storage.v1_9_1.Storage",
"Id": "storage0",
"Links": {
"StorageServices": [
{
"@odata.id": "/redfish/v1/Systems/system/Storage/storage0"
}
],
"StorageServices@odata.count": 1
},
"Name": "Storage",
"Status": {
"State": "Enabled"
}
}
wget -qO- http://localhost:80/redfish/v1/Systems/system/Storage/storage0
{
"@odata.id": "/redfish/v1/Systems/system/Storage/storage0",
"@odata.type": "#Storage.v1_9_1.Storage",
"Drives": [
{
"@odata.id": "/redfish/v1/Chassis/chassis0/Drives/drive0"
}
],
"Drives@odata.count": 1,
"Id": "storage0",
"Name": "Storage",[1]
"Status": {
"Health": "OK",
"HealthRollup": "OK",
"State": "Enabled"
},
"StorageControllers": [
{
"@odata.id": "/redfish/v1/Systems/system/Storage/storage0#/StorageControllers/0",
"@odata.type": "#Storage.v1_7_0.StorageController",
"MemberId": "controller",
"Name": "controller",
"Status": {
"Health": "OK",
"HealthRollup": "OK",
"State": "Enabled"
}
}
]
}
```
[1] https://www.snia.org/sites/default/files/technical-work/swordfish/draft/v1.2.2/pdf/Swordfish_v1.2.2_NVMeMappingGuide.pdf#page=17
Change-Id: Ib81b68e7f61b817d4dfa4ed2f27afd6e74e8ce58
Signed-off-by: Tom Tung <shes050117@gmail.com>
Signed-off-by: Willy Tu <wltu@google.com>
Signed-off-by: Ed Tanous <edtanous@google.com>
|
|
Change-Id: I2128e223f6c2d07d5c8e5a865921468a7510faf2
Signed-off-by: Ed Tanous <edtanous@google.com>
|
|
Per the input-validation rules that we follow[1], we should ALWAYS be
checking to see that there's a valid content type.
Change the default.
Tested: Only a default change, code compiles.
[1] https://cheatsheetseries.owasp.org/cheatsheets/Input_Validation_Cheat_Sheet.html
Change-Id: I4cd58a5d2fb0a49671fc5ec0398010036c743591
Signed-off-by: Ed Tanous <edtanous@google.com>
|
|
With delegate expand, the default expand level is -=
`queryCapabilities.canDelegateExpandLevel`. This creates an overlap of
expand process between delegate expand vs. default expand.
With
query.expandLevel = 2 ->
query.expandLevel = 1 and delegated.expandLevel = 1.
Both delegated and default expand will try to only expand of level one
instead of level 2 for the default.
The code in
https://github.com/openbmc/bmcweb/blob/479e899d5f57a67647f83b7f615d2c8565290bcf/redfish-core/include/utils/query_param.hpp#L583-L597
stated that the level with "@odata.id" + other property is treated as a
seperate level. So with `query.expandLevel = 1` it just loop through the
id that was already expanded and is noop.
Tested:
Before:
/redfish/v1/Chassis/BMC/Sensors?$expand=.($levels=2) returns
the same result as level=1. Needs level=3 to expand to the next level.
The RelatedItem in here doesn't get expanded with level=2.
```
wget -qO- 'http://localhost:80/redfish/v1/Chassis/BMC/Sensors?$expand=.($levels=1)'
...
{
"@odata.id": "/redfish/v1/Chassis/BMC/Sensors/temperature_DIMMXX",
"@odata.type": "#Sensor.v1_2_0.Sensor",
"Id": "temperature_DIMMXX",
"Name": "DIMMXX",
"Reading": 30.0,
"ReadingRangeMax": 127.0,
"ReadingRangeMin": -128.0,
"ReadingType": "Temperature",
"ReadingUnits": "Cel",
"RelatedItem": [
{
"@odata.id": "/redfish/v1/Systems/system/Memory/dimmXX"
}
],
"Status": {
"Health": "OK",
"State": "Enabled"
},
"Thresholds": {
"LowerCaution": {
"Reading": null
},
"LowerCritical": {
"Reading": null
},
"UpperCaution": {
"Reading": 93.0
},
"UpperCritical": {
"Reading": 95.0
}
}
}
],
"Members@odata.count": 242,
"Name": "Sensors"
}
```
After:
level=2 was able to expand to the next level.
Change-Id: I542177a94a33f8df7afbb68837f3a53b86140c86
Signed-off-by: Willy Tu <wltu@google.com>
|
|
This is one that I couldn't figure out for a while. Turns out that
fields has both a set() and an insert() method. Whereas set() replaces,
insert() appends, which is what we want in this case.
This allows us to call the actual methods several times, instead of
essentially string injecting our own code, which should make it clearer.
At the same time, there was one unit test that was structured such that
it was using addHeader to clear a header, so this commit adds an
explicit "clearHeader()" method, so we can be explicit.
Tested:
Logging into the webui in chrome (which uses POST /login) shows:
401 with no cookie header if the incorrect password is used
200 with 2 Set-Cookie headers set:
Set-Cookie:
SESSION=<session tag>; SameSite=Strict; Secure; HttpOnly
Set-Cookie:
XSRF-TOKEN=<token tag>; SameSite=Strict; Secure
Change-Id: I9b87a48ea6ba892fc08e66940563dea86edb9a65
Signed-off-by: Ed Tanous <edtanous@google.com>
|
|
The router is a giant behemoth. Start breaking it down into pieces.
Tested: Redfish service validator passes.
Signed-off-by: Ed Tanous <edtanous@google.com>
Change-Id: I9d04f53a58ffce3ecbd88dded1aa6e9648d2a762
|
|
Some logging entries are categorized as ERROR, but they would better be
as WARNING.
1)
```
$ curl -k -X GET https://${bmc}:18080/redfish/v1/Managers/bmc/LogServices/Dump/Entries/INVALID
{
....
"code": "Base.1.13.0.InternalError",
"message": "The request failed due to an internal service error. The service is still operational."
}
}%
(2023-06-01 23:29:40) [ERROR "log_services.hpp":665] Can't find Dump Entry
(2023-06-01 23:29:40) [CRITICAL "error_messages.cpp":282] Internal Error \
../../../../../../../../../bmcweb/redfish-core/lib/log_services.hpp(666:36) \
`redfish::getDumpEntryById(const std::shared_ptr<bmcweb::AsyncResp>&, const std::string&, \
const std::string&)::<lambda(const boost::system::error_code&, const dbus::utility::ManagedObjectType&)>`:
```
2)
```
$ curl -k -X GET https://${bmc}:18080/redfish/v1/UpdateService/FirmwareInventory/INVALID
(2023-05-31 15:03:38) [ERROR "update_service.hpp":1010] Input swID X1cd6ce5fZ not found!
```
Tested:
- Set bmcweb-logging=error to obtain Error or higher logs
- Run the above commands and watch out logs
- Redfish validator passed and see whether there are unexpected error or higher level logs
Change-Id: I5f14eedd68fd3454cdf2a5b2f34442a7718e718a
Signed-off-by: Myung Bae <myungbae@us.ibm.com>
|
|
In about half of our code, AsyncResp objects take the name asyncResp,
and in the other half they take the name aResp. While the difference
between them is negligeble and arbitrary, having two naming conventions
makes it more difficult to do automated changes over time via grep.
This commit was generated automtatically with the command:
git grep -l 'aResp' | xargs sed -i 's|aResp|asyncResp|g'
Tested: Code compiles.
Change-Id: Id363437b6a78f51e91cbf60aa0a0c2286f36a037
Signed-off-by: Ed Tanous <edtanous@google.com>
|
|
This code has never used strands.
Tested: Code compiles
Change-Id: I59a204fe3f3a26b2a9a8ede990335c58889fb7e6
Signed-off-by: Ed Tanous <edtanous@google.com>
|
|
With EthernetInterface 1.9.0, creation of VLAN interface is done by
POST EthernetInterfaceCollection. This patch implements the POST
handler to do so.
Tested:
* With valid RelatedInterfaces and VLANId provided, new VLAN interface
is successfully created.
* Creating VLAN over another VLAN or non-existent interface returns
error.
* Creating an existing VLAN returns ResourceAlreadyExists error.
* Invalid RelatedInterfaces links are rejected.
Change-Id: I6b1064193eccf7ec487b43139a73d9932b6eea84
Signed-off-by: Jiaqing Zhao <jiaqing.zhao@intel.com>
Signed-off-by: Ed Tanous <edtanous@google.com>
|
|
After using EthernetInterface to represent a VLAN interface, DELETE
handler is required for deleting VLAN interfaces.
Tested:
* VLAN interfaces can be deleted successfully via DELETE request.
* Deleting a physical interface returns ResourceCannotBeDeleted error.
* Deleting a non-existent interface returns ResourceNotFound error.
Change-Id: Ib22063eb3ddea0614c390ba83d4e6af29d007165
Signed-off-by: Jiaqing Zhao <jiaqing.zhao@intel.com>
Signed-off-by: Ed Tanous <edtanous@google.com>
|
|
In OpenBMC, VLAN is a virtual interface that has its own configuration
like IP address. Redfish schema 2021.2+ also suggests using individual
EthernetInterface to show VLAN information. This patch exposes VLAN
interfaces as EthernetInterface for configuring them.
Now bmcweb also shows BMC VLAN interfaces under /redfish/v1/Managers
/bmc/EthernetInterfaces.
Fixes bmcweb issue #79 (Unable configure IP on VLAN interface via
redfish).
Tested:
* Both physical and VLAN interfaces are now in the interface collection
* Only VLAN interfaces have the VLAN property and RelatedInterfaces
property pointing to its parent interface
* IP address of both physical and VLAN interfaces can be modified by
PATCH request successfully
* Redfish validator passed
Change-Id: I608892275cfbef4af8e7a03a10d67a9c2fa3ff53
Signed-off-by: Jiaqing Zhao <jiaqing.zhao@intel.com>
Signed-off-by: Ed Tanous <edtanous@google.com>
|
|
After removing all usages of VLanNetworkInterface that deprecated in
EthernetInterface 1.7.0, time to bump it to 1.9.0 for implementing
the new API design.
Tested:
Redfish validator passed.
Change-Id: Ia89d56a1325918c23ce54c9b8c0dde4342e32764
Signed-off-by: Jiaqing Zhao <jiaqing.zhao@intel.com>
|
|
In Redfish Schema (DSP2046) 2022.3 introduces EthernetInterface 1.9.0
that allows creating VLAN interface by POST EthernetInterface [1]
instead of using the deprecated VLanNetworkInterface. This patch
removes all current usage of VLanNetworkInterface.
This patchest (topic:redfish-ethernet-1.9) introduces breaking API
changes to current VLAN management features. All deprecated VLAN APIs
are removed, VLAN interfaces will be managed in the same way as the
EthernetInterface Resource, except they can be created or deleted.
Since webui-vue has not implemented anything related to VLAN yet, it
is not impacted.
Solves the issue mentioned in 188cb6294105 ("ethernet: Bump
EthernetInterface schema 1.4.1 -> 1.6.0")
[1] https://redfishforum.com/thread/619
Tested:
Redfish validator passed on a board with VLAN interface. No VLAN
interface is exposed in Redfish.
Change-Id: I9b243a5bb0f07642aa60bc13a622e862f62ee871
Signed-off-by: Jiaqing Zhao <jiaqing.zhao@intel.com>
|
|
Move Storage to v1.13.0. The Storage schema moved StorageControllers to
its own resource + collection and deprecated the existing
StorageControllers property in Storage. A link to the collection has
been added in Storage instead.
The StorageController and StorageControllerCollection are added based
on the old resource as specified in
https://redfish.dmtf.org/schemas/v1/Storage.v1_14_0.json
Added the new StorageController to remove the deprecated
`Storage/StorageControllers`. This will have the same functionility as
the existing StorageController with the exception that HealthPopulate
is not supported right now.
There will be no customer impact (other than Health resource). The
clients will now need to get the StorageController collection and then
Storagecontroller instead of directly from Storage.
Tested:
RedfishValidator passed for Storage
```
*** /redfish/v1/Systems/system/Storage
INFO - Attempt 1 of /redfish/v1/Systems/system/Storage
INFO - Response Time for GET to /redfish/v1/Systems/system/Storage: 0.04373445897363126 seconds.
INFO - Type (StorageCollection.StorageCollection), GET SUCCESS (time: 0:00:00.044128)
INFO - Attempt 1 of /redfish/v1/Systems/system/Storage/1
INFO - Response Time for GET to /redfish/v1/Systems/system/Storage/1: 0.3353928590659052 seconds.
INFO - PASS
INFO -
*** /redfish/v1/Systems/system/Storage/1
INFO - Type (Storage.v1_13_0.Storage), GET SUCCESS (time: 0:00:00.335720)
*** /redfish/v1/Systems/system/Storage/1/Controllers
INFO - Type (StorageControllerCollection.StorageControllerCollection), GET SUCCESS (time: 0:00:00.046414)
INFO - Attempt 1 of /redfish/v1/Systems/system/Storage/1/Controllers/cpld
INFO - Response Time for GET to /redfish/v1/Systems/system/Storage/1/Controllers/cpld: 0.05196243803948164 seconds.
INFO - Attempt 1 of /redfish/v1/Systems/system/Storage/1/Controllers/morristown
INFO - Response Time for GET to /redfish/v1/Systems/system/Storage/1/Controllers/morristown: 0.05082511808723211 seconds.
INFO - PASS
INFO -
...
*** /redfish/v1/Systems/system/Storage/1/Controllers/controller_0
INFO - Type (StorageController.v1_6_0.StorageController), GET SUCCESS (time: 0:00:00.052223)
INFO - PASS
INFO -
*** /redfish/v1/Systems/system/Storage/1/Controllers/controller_1
INFO - Type (StorageController.v1_6_0.StorageController), GET SUCCESS (time: 0:00:00.051165)
INFO - PASS
INFO -
```
```
wget -qO - http://localhost:80/redfish/v1/Systems/system/Storage/1/Controllers
{
"@odata.id": "/redfish/v1/Systems/system/Storage/1/Controllers",
"@odata.type": "#StorageControllerCollection.StorageControllerCollection",
"Members": [
{
"@odata.id": "/redfish/v1/Systems/system/Storage/1/Controllers/controller_0"
},
{
"@odata.id": "/redfish/v1/Systems/system/Storage/1/Controllers/controller_1"
}
],
"Members@odata.count": 2,
"Name": "Storage Controller Collection"
}
```
```
wget -qO - http://localhost:80/redfish/v1/Systems/system/Storage/1/Controllers/controller_1
{
"@odata.id":
"/redfish/v1/Systems/system/Storage/1/Controllers/controller_1",
"@odata.type": "#StorageController.v1_6_0.StorageController",
"Id": "cpld",
"Name": "cpld",
"Status": {
"State": "Enabled"
}
}
```
Change-Id: I1c171514d5613f93d283d764ffb69b16dc3ba74d
Signed-off-by: Willy Tu <wltu@google.com>
|
|
Convert all types to uint8_t to not hit the conversion warning.
Change-Id: Ia535ca0a2f4045cbde06a2f8f8eaad9570a0f4a5
Signed-off-by: Willy Tu <wltu@google.com>
Signed-off-by: Ed Tanous <edtanous@google.com>
|
|
All new uses should be using boost::urls::url now. This was the last
usage.
Tested: Logged into webui, and observed the correct URL behavior.
In browser window /foobar
Forwarded to /?next=/foobar#/login
Which is correct.
Note, this is different behavior slightly than before. It was found
that the URI precedence goes query string THEN fragment, rather than the
other way around that we had it. This was flagged when moving over to
boost url structures.
Signed-off-by: Ed Tanous <edtanous@google.com>
Change-Id: Ifb354537d71a43c531d7d380dd889cf646731e39
|
|
std::string::data now has a non-const variation in c++20. This allows
us to remove a NOLINT and follow the standard.
Tested: Login succeeds.
Change-Id: Ie49932fae8efa90afe1a238f7059924747300521
Signed-off-by: Ed Tanous <edtanous@google.com>
|
|
This drop adds support to specify AccountTypes at the time of user
creation. Made sure that HostConsole is only supported for user with
administrator role.
Testing:
$ curl -k -X POST https://${bmc}/redfish/v1/AccountService/Accounts
-d '{"UserName": "user99", "Password": "0penBmc0", "RoleId":
"Administrator", "AccountTypes": ["HostConsole"]}'
{
"@Message.ExtendedInfo": [
{
"@odata.type": "#Message.v1_1_1.Message",
"Message": "The resource has been created successfully.",
"MessageArgs": [],
"MessageId": "Base.1.13.0.Created",
"MessageSeverity": "OK",
"Resolution": "None."
}
]
}
$ curl -k
https://root:0penBmc@bmc1:443/redfish/v1/AccountService/Accounts/user99
{
"@odata.id": "/redfish/v1/AccountService/Accounts/user99",
"@odata.type": "#ManagerAccount.v1_7_0.ManagerAccount",
"AccountTypes": [
"HostConsole"
],
"Description": "User Account",
"Enabled": true,
"Id": "user99",
"Links": {
"Role": {
"@odata.id": "/redfish/v1/AccountService/Roles/Administrator"
}
},
"Locked": false,
"Locked@Redfish.AllowableValues": [
"false"
],
"Name": "User Account",
"Password": null,
"PasswordChangeRequired": false,
"RoleId": "Administrator",
"StrictAccountTypes": true,
"UserName": "user99"
}
Also ran following testcases:
$ curl -k -X POST https://${bmc}/redfish/v1/AccountService/Accounts
-d '{"UserName": "user99", "Password": "0penBmc0", "RoleId":
"Administrator", "AccountTypes": ["HostConsole"]}'
$ curl -k -X POST https://${bmc}/redfish/v1/AccountService/Accounts
-d '{"UserName": "user99", "Password": "0penBmc0", "RoleId":
"Operator", "AccountTypes": ["HostConsole"]}'
$ curl -k -X POST https://${bmc}/redfish/v1/AccountService/Accounts
-d '{"UserName": "user99", "Password": "0penBmc0", "RoleId":
"ReadOnly", "AccountTypes": ["HostConsole"]}'
$ curl -k -X POST https://${bmc}/redfish/v1/AccountService/Accounts
-d '{"UserName": "user99", "Password": "0penBmc0", "RoleId":
"Administrator", "AccountTypes": ["ManagerConsole"]}'
$ curl -k -X POST https://${bmc}/redfish/v1/AccountService/Accounts
-d '{"UserName": "user99", "Password": "0penBmc0", "RoleId":
"Administrator", "AccountTypes": ["Redfish"]}'
$ curl -k -X POST https://${bmc}/redfish/v1/AccountService/Accounts
-d '{"UserName": "user99", "Password": "0penBmc0", "RoleId":
"Administrator", "AccountTypes": ["WebUI"]}'
$ curl -k -X POST https://${bmc}/redfish/v1/AccountService/Accounts
-d '{"UserName": "user99", "Password": "0penBmc0", "RoleId":
"Administrator", "AccountTypes": ["IPMI"]}'
$ curl -k -X POST https://${bmc}/redfish/v1/AccountService/Accounts
-d '{"UserName": "user99", "Password": "0penBmc0", "RoleId":
"Administrator", "AccountTypes": ["Redfish", "IPMI", "HostConsole",
"ManagerConsole", "WebUI"]}'
$ curl -k -X POST https://${bmc}/redfish/v1/AccountService/Accounts
-d '{"UserName": "user99", "Password": "0penBmc0", "RoleId":
"Administrator", "AccountTypes": ["Redfish", "HostConsole",
"ManagerConsole", "WebUI"]}'
$ curl -k -X POST https://${bmc}/redfish/v1/AccountService/Accounts
-d '{"UserName": "user99", "Password": "0penBmc0", "RoleId":
"Administrator", "AccountTypes": ["Redfish", "ManagerConsole",
"WebUI"]}'
$ curl -k -X POST https://${bmc}/redfish/v1/AccountService/Accounts
-d '{"UserName": "user99", "Password": "0penBmc0", "RoleId":
"Administrator", "AccountTypes": ["Redfish", "HostConsole", "WebUI"]}'
$ curl -k -X POST https://${bmc}/redfish/v1/AccountService/Accounts
-d '{"UserName": "user99", "Password": "0penBmc0", "RoleId":
"Administrator", "AccountTypes": ["IPMI", "HostConsole",
"ManagerConsole"]}'
$ curl -k -X POST https://${bmc}/redfish/v1/AccountService/Accounts
-d '{"UserName": "user99", "Password": "0penBmc0", "RoleId":
"Operator", "AccountTypes": ["Redfish", "ManagerConsole", "WebUI"]}'
$ curl -k -X POST https://${bmc}/redfish/v1/AccountService/Accounts
-d '{"UserName": "user99", "Password": "0penBmc0", "RoleId":
"ReadOnly", "AccountTypes": ["Redfish", "ManagerConsole", "WebUI"]}'
Change-Id: I19ff994e712bcfaf827a5f8dd02a752a6ab92214
Signed-off-by: Ninad Palsule <ninadpalsule@us.ibm.com>
|
|
This commit enhances the redfish API to set and unset userGroups
information for each user account.
Users with ConfigureUsers level privilege can patch (Set and Unset)
AccountTypes of each user role. In addition, a user with
"ConfigureSelf" level privilege can only set or Update their password.
"Redfish" is always enabled in each user role. However,
"ConfigureUsers" can disable other user redfish services. But if
"ConfigureUsers" try to disable its redfish service, that generates an
error.
In this commit, users can enable and disable "redfish", "ssh",
"hostconsole" and "ipmi" services from each user where ssh is a special
case.
The 'web' group does not control access to the web interface, and
doesn't appear to do anything. The 'redfish' in the UserGroups is
mapped to both Redfish and WebUI AccountTypes. To enable redfish
User Group both of these account types should be specified, and none
to disable it.
Tested:
Testing was done using curl command with ConfigureUsers and
ConfigureSelf.
$ curl -k -X PATCH
https://$bmc:18080/redfish/v1/AccountService/Accounts/webuser -d
'{"AccountTypes": ["Redfish", "WebUI", "ManagerConsole",
"HostConsole"]}'
{
"@Message.ExtendedInfo": [
{
"@odata.type": "#Message.v1_1_1.Message",
"Message": "The request completed successfully.",
"MessageArgs": [],
"MessageId": "Base.1.13.0.Success",
"MessageSeverity": "OK",
"Resolution": "None"
}
]
}
Also ran following cases:
$ curl -k -X PATCH
https://${bmc}/redfish/v1/AccountService/Accounts/user99
-d '{"AccountTypes": ["HostConsole"]}'
$ curl -k -X PATCH
https://${bmc}/redfish/v1/AccountService/Accounts/user99
-d '{"AccountTypes": ["IPMI"]}'
$ curl -k -X PATCH
https://${bmc}/redfish/v1/AccountService/Accounts/user99
-d '{"AccountTypes": ["Redfish", "WebUI"]}'
$ curl -k -X PATCH
https://${bmc}/redfish/v1/AccountService/Accounts/user99
-d '{"AccountTypes": ["ManagerConsole"]}'
$ curl -k -X PATCH
https://${bmc}/redfish/v1/AccountService/Accounts/user99
-d '{"AccountTypes": ["Redfish", "IPMI", "HostConsole",
"ManagerConsole", "WebUI"]}'
{
"error": {
"@Message.ExtendedInfo": [
{
"@odata.type": "#Message.v1_1_1.Message",
"Message": "There are insufficient privileges for the account or
credentials associated with the current session to
perform the requested operation.",
"MessageArgs": [],
"MessageId": "Base.1.13.0.InsufficientPrivilege",
"MessageSeverity": "Critical",
"Resolution": "Either abandon the operation or change the
associated access rights and resubmit the request if the
operation failed."
}
],
"code": "Base.1.13.0.InsufficientPrivilege",
"message": "There are insufficient privileges for the account or
credentials associated with the current session to
perform the requested operation."
}
$ curl -k -H 'X-Auth-Token: IpnCBj1Lozh53Jhzxu7T' -X PATCH
https://${bmc}/redfish/v1/AccountService/Accounts/user999
-d '{"Password":"0penBmc123"}'
{
"@Message.ExtendedInfo": [
{
"@odata.type": "#Message.v1_1_1.Message",
"Message": "The request completed successfully.",
"MessageArgs": [],
"MessageId": "Base.1.13.0.Success",
"MessageSeverity": "OK",
"Resolution": "None"
}
]
Signed-off-by: Ninad Palsule <ninadpalsule@us.ibm.com>
Signed-off-by: Abhishek Patel <Abhishek.Patel@ibm.com>
Change-Id: I1a0344ca45556b820bb77c3dcb459f27eb032501
Signed-off-by: Shantappa Teekappanavar <shantappa.teekappanavar@ibm.com>
|
|
A new feature has been proposed[1] and implemented[2] which can be
optionally enabled on a system to not allow a chassis or host power on
operation when the BMC is not in a "Ready" state.
In those situations, if a power on operation is requested, the D-Bus
error response will be a specific BMCNotReady error. In those
situations, respond to the user with a more targeted error asking them
to retry in 10 seconds. The 10s retry is based on my experience with
OpenBMC based systems, the longest time between bmcweb being up and
running and BMC Ready is around 30s.
Tested:
- Enabled BMC Ready feature, manually put BMC in NotReady state,
and requested a:
```
/redfish/v1/Chassis/chassis/Actions/Chassis.Reset -d '{"ResetType": "PowerCycle"}'
```
- Confirmed new response message:
```
"Message": "The service is temporarily unavailable. Retry in 10 seconds."
```
- Stopped Chassis state service and verified expected "internal service
error" on same request
- Ran similar test with Systems/system/Actions/ComputerSystem.Reset API
- Confirmed good paths still worked as expected
[1]: https://lists.ozlabs.org/pipermail/openbmc/2023-May/033383.html
[2]: https://gerrit.openbmc.org/q/topic:bmc-ready-check
Change-Id: I6a6e5774c96b4f37c794ba49a5e06d3e51156d09
Signed-off-by: Andrew Geissler <geissonator@yahoo.com>
|
|
This extra quote snuch into the comment somehow. Fix it.
Change-Id: I5aa14e1f43b1de9cabda006f7f9727d611c5aea3
Signed-off-by: Ed Tanous <edtanous@google.com>
|
|
NOTICE: Future-deprecated features used:
* 0.64.0: {'copy arg in configure_file'}
Port to the new version of this, which is install_data.
Tested: Meson configure no longer throws the above warning.
Signed-off-by: Ed Tanous <edtanous@google.com>
Change-Id: I9c4d91c3c45bc52dde69ca34d7fc686e2e6c0d74
|
|
c9374ff613b6836010877f8083e75657abc78343
Was recently checked in that adds subproject files for nlohmann. It
opted for the name "nlohmann" when the upstream project installs itself
as "nlohmann_json". This mismatch causes a yocto build failure.
Fix it.
Tested: Code compiles
Change-Id: I2df5f473a61172593e9a1045fc5a229e06e3afc2
Signed-off-by: Ed Tanous <edtanous@google.com>
|