ironic can't find nodes whose name ends in ".<mimetype>"

Bug #1643995 reported by Andrew Balfour on 2016-11-22
This bug affects 1 person
Affects Status Importance Assigned to Milestone
Fix Released
Ruby Loo

Bug Description

# Create a new node
% ironic node-create -d fake --name
| Property | Value |
| chassis_uuid | |
| driver | fake |
| driver_info | {} |
| extra | {} |
| name | |
| properties | {} |
| uuid | 78970da1-1621-4168-9062-e18bdcfc1836 |

# Display it
% ironic node-show
Not Found (HTTP 404)

# Erm. It's there:
% ironic node-list | grep
| 78970da1-1621-4168-9062-e18bdcfc1836 | | None | None | available | False |

# And we can get it by uuid:
% ironic node-show 78970da1-1621-4168-9062-e18bdcfc1836 | grep name
| name |

# It appears to be short circuiting somewhere before getting to ironic, as ironic returns a
# different 404:
% ironic node-show nosuchnode
Node nosuchnode could not be found. (HTTP 404)

Eventually we traverse far enough down the rabbit hole to pecan/ where in find_controller(), it tries to be helpful:

425 def find_controller(self, state):
426 '''
427 The main request handler for Pecan applications.
428 '''
445 # attempt to guess the content type based on the file extension
446 if self.guess_content_type_from_ext \
447 and not pecan_state['content_type'] \
448 and '.' in path:
449 _, extension = splitext(path.rstrip('/'))
451 # preface with a letter to ensure compat for 2.5
452 potential_type = guess_type('x' + extension)[0]
454 if extension and potential_type is not None:
455 path = ''.join(path.rsplit(extension, 1))
456 pecan_state['extension'] = extension
457 pecan_state['content_type'] = potential_type

(Pdb) p self.guess_content_type_from_ext
(Pdb) p pecan_state
{'routing_path': u'/v1/nodes/', 'content_type': None}
(Pdb) p path

So we fall in and hit 449, which sets extension to ".me", which is apparently the mime type for:

(Pdb) p potential_type

So we fall into 454. This sets us up for failure at 510:

510 elif cfg.get('content_type') is not None and \
511 pecan_state['content_type'] not in content_types:
513 msg = "Controller '%s' defined does not support content_type " + \
514 "'%s'. Supported type(s): %s"
515 logger.error(
516 msg % (
517 controller.__name__,
518 pecan_state['content_type'],
519 content_types.keys()
520 )
521 )
522 -> raise exc.HTTPNotFound

because pecan is configured only to handle "application/json" requests:

(Pdb) p cfg['content_type']

and we've explicitly set the content type of this request to 'application/x-troff-me' it returns 404 at 522.

So, basically, if your nodename ends in ".<mimetype>", pecan won't let you see it because it never passes the request off to ironic:

% ironic node-update 78970da1-1621-4168-9062-e18bdcfc1836 replace name=test.mp3 > /dev/null
% ironic node-show test.mp3
Not Found (HTTP 404)
% ironic node-update 78970da1-1621-4168-9062-e18bdcfc1836 replace name=test.doc > /dev/null
% ironic node-show test.doc
Not Found (HTTP 404)
% ironic node-update 78970da1-1621-4168-9062-e18bdcfc1836 replace name=test.html > /dev/null
% ironic node-show test.html
Not Found (HTTP 404)
% ironic node-update 78970da1-1621-4168-9062-e18bdcfc1836 replace name=test.ok > /dev/null
% ironic node-show test.ok | grep name
| name | test.ok

A workaround would appear to be getting pecan's guess_content_type_from_ext option turned off by hacking it into both ironic/api/ and ironic/api/


--- /usr/lib/python2.7/vendor-packages/ironic/api/ 2016-06-21 12:57:28.000000000 -0700
+++ /usr/lib/python2.7/vendor-packages/ironic/api/ 2016-11-22 11:34:56.142128817 -0800
@@ -27,6 +27,7 @@
     'static_root': '%(confdir)s/public',
     'debug': False,
     'enable_acl': True,
+ 'guess_content_type_from_ext': False,
     'acl_public_routes': [
--- /usr/lib/python2.7/vendor-packages/ironic/api/ 2016-06-21 12:57:24.000000000 -0700
+++ /usr/lib/python2.7/vendor-packages/ironic/api/ 2016-11-22 11:41:39.120103565 -0800
@@ -54,7 +54,6 @@
     filename = config.__file__.replace('.pyc', '.py')
     return pecan.configuration.conf_from_file(filename)

 def setup_app(pecan_config=None, extra_hooks=None):
     app_hooks = [hooks.ConfigHook(),
@@ -78,6 +77,7 @@,
         force_canonical=getattr(, 'force_canonical', True),
+ guess_content_type_from_ext=getattr(, 'guess_content_type_from_ext', True),

Vladyslav Drok (vdrok) on 2016-11-23
Changed in ironic:
status: New → Confirmed
importance: Undecided → Medium
Vladyslav Drok (vdrok) wrote :

Thanks for reporting! The proposed fix should work. We can't microversion this change, so seems that we should set it to False by default, with some deprecation warning maybe? And after some time just always set guess_content_type_from_ext=True, as such possibility does not make much sense anyway - ironic supports only json content-type. Not really sure how to do that deprecation, apart from release note mention...

Vadim Hmyrov (vhmyrov) on 2016-12-02
Changed in ironic:
assignee: nobody → Vadim Hmyrov (vhmyrov)
Changed in ironic:
status: Confirmed → Triaged

Fix proposed to branch: master

Changed in ironic:
status: Triaged → In Progress
Kyrylo Romanenko (kromanenko) wrote :

This bug also affects command `ironic node-delete`

Kyrylo Romanenko (kromanenko) wrote :

Named portgroups are also affected. I suppose that drivers too.

Changed in ironic:
assignee: Vadim Hmyrov (vhmyrov) → Galyna Zholtkevych (gzholtkevych)
Vladyslav Drok (vdrok) on 2017-11-01
Changed in ironic:
assignee: Galyna Zholtkevych (gzholtkevych) → nobody
status: In Progress → Triaged
Dmitry Tantsur (divius) wrote :

Raising to high, as it seems to affect productions:

Changed in ironic:
importance: Medium → High
Dmitry Tantsur (divius) on 2018-02-06
Changed in ironic:
assignee: nobody → Dmitry Tantsur (divius)

Fix proposed to branch: master

Changed in ironic:
status: Triaged → In Progress

Change abandoned by Dmitry Tantsur (<email address hidden>) on branch: master
Reason: I ended up with a complete change of the approach, see

Changed in ironic:
assignee: Dmitry Tantsur (divius) → Ruby Loo (rloo)

Submitter: Zuul
Branch: master

commit cfc167eadfc373b262d92d8ce4a00a9fd38d23d7
Author: Dmitry Tantsur <email address hidden>
Date: Tue Feb 6 14:58:20 2018 +0100

    Stop guessing mime types based on URLs

    Currently we have a pecan feature enabled that strips extensions
    from the end of the URL and treat it like requested content type.
    E.g. /v1/nodes.json is treated as /v1/nodes with requested content
    type Application/Json. However, this prevents certain node names:
    e.g. /v1/nodes/small.1 is treated like /v1/nodes/small with content
    type of a man page. It does not make any sense for ironic API,
    as we only support Application/Json content type (and .json suffix).

    This change disabled this pecan feature. To keep backward compability
    a new middleware stips the .json prefix and saves a flag in the
    environment about its presence. API accepting names try to find
    their resource first without, then with .json suffix.

    The following endpoints are special-cased to support names with .json:
    * Node GET, PATCH and DELETE
    * Ramdisk heartbeat
    * Port group GET, PATCH and DELETE

    VIF API is not updated, so VIF IDs still cannot have .json suffix.

    Change-Id: I789ecfeac9b64a9c4105a20619f7bf5dfc133189
    Closes-Bug: #1643995

Changed in ironic:
status: In Progress → Fix Released

This issue was fixed in the openstack/ironic 10.1.0 release.

To post a comment you must log in.
This report contains Public information  Edit
Everyone can see this information.

Other bug subscribers

Remote bug watches

Bug watches keep track of this bug in other bug trackers.