Unable to append OS to config_template via API

I am trying to do the equivalent of associating an OS to a provisioning
template via the API.

I am doing this in python and have the following:

r = requests.get(foreman_api +'/operatingsystems/' + dev_os_id,
params=data, auth=(user, password), headers=headers, verify=False)
config_templates = json.loads(r.text)['config_templates']
for template in config_templates:
template_id = str(template['id'])
r = requests.get(foreman_api +'/config_templates/' + template_id,
auth=(user, password), headers=headers, verify=False)
associated_oses = json.loads(r.text)['operatingsystems']
data = '{ "title": "'+ os_name +'", "id": '+ os_id +', "name": "OS" }'
print data
associated_oses.append(json.loads(data))
data = json.dumps({
"operatingsystems": associated_oses
})
print data
r = requests.put(foreman_api +'/config_templates/'+ template_id, data,
auth=(user, password), headers=headers, verify=False)
if ( r.status_code != 200 ):
print "Unable to associate OS to template"
print r.status_code
print r.text
print json.loads(r.text)['error']['full_messages']
sys.exit(1)

But when I run, it fails and I get :
Unable to associate OS to template
500
{
"error": {"message":"undefined method `merge!' for nil:NilClass"}
}

I don't see anything wrong with my request though the list.append operation
appears to reorder the new OS dict keys to become id, name, title.

Anyone have any suggestions on what I may be doing wrong here?

Thanks,
Ryan

Here's the full stack trace from the log file

2015-10-01T16:01:13 [app] [W] Action failed
> NoMethodError: undefined method merge!' for nil:NilClass > /usr/share/foreman/app/controllers/api/v2/base_controller.rb:119:inblock in append_array_of_ids'
> /usr/share/foreman/app/controllers/api/v2/base_controller.rb:114:in
each' > /usr/share/foreman/app/controllers/api/v2/base_controller.rb:114:inappend_array_of_ids'
> /usr/share/foreman/app/controllers/api/v2/base_controller.rb:132:in
setup_has_many_params' > /usr/share/foreman/vendor/ruby/1.9.1/gems/activesupport-3.2.21/lib/active_support/callbacks.rb:532:inblock (4 levels) in
_run__1380515315320750019__process_action__3057254880273293609__callbacks'
>
/usr/share/foreman/vendor/ruby/1.9.1/gems/activesupport-3.2.21/lib/active_support/callbacks.rb:215:in
block in _conditional_callback_around_7668' > /usr/share/foreman/vendor/ruby/1.9.1/gems/activesupport-3.2.21/lib/active_support/callbacks.rb:326:inaround'
>
/usr/share/foreman/vendor/ruby/1.9.1/gems/activesupport-3.2.21/lib/active_support/callbacks.rb:310:in
_callback_around_2537' > /usr/share/foreman/vendor/ruby/1.9.1/gems/activesupport-3.2.21/lib/active_support/callbacks.rb:214:in_conditional_callback_around_7668'
>
/usr/share/foreman/vendor/ruby/1.9.1/gems/activesupport-3.2.21/lib/active_support/callbacks.rb:527:in
block (3 levels) in _run__1380515315320750019__process_action__3057254880273293609__callbacks' > /usr/share/foreman/vendor/ruby/1.9.1/gems/activesupport-3.2.21/lib/active_support/callbacks.rb:215:inblock in _conditional_callback_around_7667'
> /usr/share/foreman/app/controllers/concerns/application_shared.rb:13:in
set_timezone' > /usr/share/foreman/vendor/ruby/1.9.1/gems/activesupport-3.2.21/lib/active_support/callbacks.rb:214:in_conditional_callback_around_7667'
>
/usr/share/foreman/vendor/ruby/1.9.1/gems/activesupport-3.2.21/lib/active_support/callbacks.rb:526:in
block (2 levels) in _run__1380515315320750019__process_action__3057254880273293609__callbacks' > /usr/share/foreman/vendor/ruby/1.9.1/gems/activesupport-3.2.21/lib/active_support/callbacks.rb:215:inblock in _conditional_callback_around_7666'
> /usr/share/foreman/app/models/concerns/foreman/thread_session.rb:32:in
clear_thread' > /usr/share/foreman/vendor/ruby/1.9.1/gems/activesupport-3.2.21/lib/active_support/callbacks.rb:214:in_conditional_callback_around_7666'
>
/usr/share/foreman/vendor/ruby/1.9.1/gems/activesupport-3.2.21/lib/active_support/callbacks.rb:448:in
block in _run__1380515315320750019__process_action__3057254880273293609__callbacks' > /usr/share/foreman/vendor/ruby/1.9.1/gems/activesupport-3.2.21/lib/active_support/callbacks.rb:215:inblock in _conditional_callback_around_7665'
>
/usr/share/foreman/vendor/ruby/1.9.1/gems/activesupport-3.2.21/lib/active_support/callbacks.rb:326:in
around' > /usr/share/foreman/vendor/ruby/1.9.1/gems/activesupport-3.2.21/lib/active_support/callbacks.rb:310:in_callback_around_13'
>
/usr/share/foreman/vendor/ruby/1.9.1/gems/activesupport-3.2.21/lib/active_support/callbacks.rb:214:in
_conditional_callback_around_7665' > /usr/share/foreman/vendor/ruby/1.9.1/gems/activesupport-3.2.21/lib/active_support/callbacks.rb:414:in_run__1380515315320750019__process_action__3057254880273293609__callbacks'
>
/usr/share/foreman/vendor/ruby/1.9.1/gems/activesupport-3.2.21/lib/active_support/callbacks.rb:405:in
__run_callback' > /usr/share/foreman/vendor/ruby/1.9.1/gems/activesupport-3.2.21/lib/active_support/callbacks.rb:385:in_run_process_action_callbacks'
>
/usr/share/foreman/vendor/ruby/1.9.1/gems/activesupport-3.2.21/lib/active_support/callbacks.rb:81:in
run_callbacks' > /usr/share/foreman/vendor/ruby/1.9.1/gems/actionpack-3.2.21/lib/abstract_controller/callbacks.rb:17:inprocess_action'
>
/usr/share/foreman/vendor/ruby/1.9.1/gems/actionpack-3.2.21/lib/action_controller/metal/rescue.rb:29:in
process_action' > /usr/share/foreman/vendor/ruby/1.9.1/gems/actionpack-3.2.21/lib/action_controller/metal/instrumentation.rb:30:inblock in process_action'
>
/usr/share/foreman/vendor/ruby/1.9.1/gems/activesupport-3.2.21/lib/active_support/notifications.rb:123:in
block in instrument' > /usr/share/foreman/vendor/ruby/1.9.1/gems/activesupport-3.2.21/lib/active_support/notifications/instrumenter.rb:20:ininstrument'
>
/usr/share/foreman/vendor/ruby/1.9.1/gems/activesupport-3.2.21/lib/active_support/notifications.rb:123:in
instrument' > /usr/share/foreman/vendor/ruby/1.9.1/gems/actionpack-3.2.21/lib/action_controller/metal/instrumentation.rb:29:inprocess_action'
>
/usr/share/foreman/vendor/ruby/1.9.1/gems/actionpack-3.2.21/lib/action_controller/metal/params_wrapper.rb:207:in
process_action' > /usr/share/foreman/vendor/ruby/1.9.1/gems/activerecord-3.2.21/lib/active_record/railties/controller_runtime.rb:18:inprocess_action'
>
/usr/share/foreman/vendor/ruby/1.9.1/gems/actionpack-3.2.21/lib/abstract_controller/base.rb:121:in
process' > /usr/share/foreman/vendor/ruby/1.9.1/gems/actionpack-3.2.21/lib/abstract_controller/rendering.rb:45:inprocess'
>
/usr/share/foreman/vendor/ruby/1.9.1/gems/actionpack-3.2.21/lib/action_controller/metal.rb:203:in
dispatch' > /usr/share/foreman/vendor/ruby/1.9.1/gems/actionpack-3.2.21/lib/action_controller/metal/rack_delegation.rb:14:indispatch'
>
/usr/share/foreman/vendor/ruby/1.9.1/gems/actionpack-3.2.21/lib/action_controller/metal.rb:246:in
block in action' > /usr/share/foreman/vendor/ruby/1.9.1/gems/actionpack-3.2.21/lib/action_dispatch/routing/route_set.rb:73:incall'
>
/usr/share/foreman/vendor/ruby/1.9.1/gems/actionpack-3.2.21/lib/action_dispatch/routing/route_set.rb:73:in
dispatch' > /usr/share/foreman/vendor/ruby/1.9.1/gems/actionpack-3.2.21/lib/action_dispatch/routing/route_set.rb:36:incall'
>
/usr/share/foreman/vendor/ruby/1.9.1/gems/actionpack-3.2.21/lib/action_dispatch/routing/mapper.rb:43:in
call' > /usr/share/foreman/vendor/ruby/1.9.1/gems/journey-1.0.4/lib/journey/router.rb:68:inblock in call'
>
/usr/share/foreman/vendor/ruby/1.9.1/gems/journey-1.0.4/lib/journey/router.rb:56:in
each' > /usr/share/foreman/vendor/ruby/1.9.1/gems/journey-1.0.4/lib/journey/router.rb:56:incall'
>
/usr/share/foreman/vendor/ruby/1.9.1/gems/actionpack-3.2.21/lib/action_dispatch/routing/route_set.rb:608:in
call' > /usr/share/foreman/vendor/ruby/1.9.1/gems/apipie-rails-0.2.6/lib/apipie/extractor/recorder.rb:97:incall'
>
/usr/share/foreman/vendor/ruby/1.9.1/gems/apipie-rails-0.2.6/lib/apipie/middleware/checksum_in_headers.rb:27:in
call' > /usr/share/foreman/vendor/ruby/1.9.1/gems/actionpack-3.2.21/lib/action_dispatch/middleware/best_standards_support.rb:17:incall'
>
/usr/share/foreman/vendor/ruby/1.9.1/gems/rack-1.4.7/lib/rack/etag.rb:23:in
call' > /usr/share/foreman/vendor/ruby/1.9.1/gems/rack-1.4.7/lib/rack/conditionalget.rb:35:incall'
>
/usr/share/foreman/vendor/ruby/1.9.1/gems/actionpack-3.2.21/lib/action_dispatch/middleware/head.rb:14:in
call' > /usr/share/foreman/vendor/ruby/1.9.1/gems/actionpack-3.2.21/lib/action_dispatch/middleware/params_parser.rb:21:incall'
> /usr/share/foreman/lib/middleware/catch_json_parse_errors.rb:9:in call' > /usr/share/foreman/vendor/ruby/1.9.1/gems/actionpack-3.2.21/lib/action_dispatch/middleware/flash.rb:242:incall'
>
/usr/share/foreman/vendor/ruby/1.9.1/gems/rack-1.4.7/lib/rack/session/abstract/id.rb:210:in
context' > /usr/share/foreman/vendor/ruby/1.9.1/gems/rack-1.4.7/lib/rack/session/abstract/id.rb:205:incall'
>
/usr/share/foreman/vendor/ruby/1.9.1/gems/actionpack-3.2.21/lib/action_dispatch/middleware/cookies.rb:341:in
call' > /usr/share/foreman/vendor/ruby/1.9.1/gems/activerecord-3.2.21/lib/active_record/query_cache.rb:64:incall'
>
/usr/share/foreman/vendor/ruby/1.9.1/gems/activerecord-3.2.21/lib/active_record/connection_adapters/abstract/connection_pool.rb:479:in
call' > /usr/share/foreman/vendor/ruby/1.9.1/gems/actionpack-3.2.21/lib/action_dispatch/middleware/callbacks.rb:28:inblock in call'
>
/usr/share/foreman/vendor/ruby/1.9.1/gems/activesupport-3.2.21/lib/active_support/callbacks.rb:405:in
_run__1125176928877678019__call__4296426048178578494__callbacks' > /usr/share/foreman/vendor/ruby/1.9.1/gems/activesupport-3.2.21/lib/active_support/callbacks.rb:405:in__run_callback'
>
/usr/share/foreman/vendor/ruby/1.9.1/gems/activesupport-3.2.21/lib/active_support/callbacks.rb:385:in
_run_call_callbacks' > /usr/share/foreman/vendor/ruby/1.9.1/gems/activesupport-3.2.21/lib/active_support/callbacks.rb:81:inrun_callbacks'
>
/usr/share/foreman/vendor/ruby/1.9.1/gems/actionpack-3.2.21/lib/action_dispatch/middleware/callbacks.rb:27:in
call' > /usr/share/foreman/vendor/ruby/1.9.1/gems/actionpack-3.2.21/lib/action_dispatch/middleware/remote_ip.rb:31:incall'
>
/usr/share/foreman/vendor/ruby/1.9.1/gems/actionpack-3.2.21/lib/action_dispatch/middleware/debug_exceptions.rb:16:in
call' > /usr/share/foreman/vendor/ruby/1.9.1/gems/actionpack-3.2.21/lib/action_dispatch/middleware/show_exceptions.rb:56:incall'
>
/usr/share/foreman/vendor/ruby/1.9.1/gems/railties-3.2.21/lib/rails/rack/logger.rb:32:in
call_app' > /usr/share/foreman/vendor/ruby/1.9.1/gems/railties-3.2.21/lib/rails/rack/logger.rb:18:incall'
>
/usr/share/foreman/vendor/ruby/1.9.1/gems/actionpack-3.2.21/lib/action_dispatch/middleware/request_id.rb:22:in
call' > /usr/share/foreman/vendor/ruby/1.9.1/gems/rack-1.4.7/lib/rack/methodoverride.rb:21:incall'
>
/usr/share/foreman/vendor/ruby/1.9.1/gems/rack-1.4.7/lib/rack/runtime.rb:17:in
call' > /usr/share/foreman/vendor/ruby/1.9.1/gems/activesupport-3.2.21/lib/active_support/cache/strategy/local_cache.rb:72:incall'
>
/usr/share/foreman/vendor/ruby/1.9.1/gems/rack-1.4.7/lib/rack/lock.rb:15:in
call' > /usr/share/foreman/vendor/ruby/1.9.1/gems/actionpack-3.2.21/lib/action_dispatch/middleware/static.rb:83:incall'
>
/usr/share/foreman/vendor/ruby/1.9.1/gems/rack-cache-1.2/lib/rack/cache/context.rb:136:in
forward' > /usr/share/foreman/vendor/ruby/1.9.1/gems/rack-cache-1.2/lib/rack/cache/context.rb:143:inpass'
>
/usr/share/foreman/vendor/ruby/1.9.1/gems/rack-cache-1.2/lib/rack/cache/context.rb:155:in
invalidate' > /usr/share/foreman/vendor/ruby/1.9.1/gems/rack-cache-1.2/lib/rack/cache/context.rb:71:incall!'
>
/usr/share/foreman/vendor/ruby/1.9.1/gems/rack-cache-1.2/lib/rack/cache/context.rb:51:in
call' > /usr/share/foreman/vendor/ruby/1.9.1/gems/railties-3.2.21/lib/rails/engine.rb:484:incall'
>
/usr/share/foreman/vendor/ruby/1.9.1/gems/railties-3.2.21/lib/rails/application.rb:231:in
call' > /usr/share/foreman/vendor/ruby/1.9.1/gems/railties-3.2.21/lib/rails/railtie/configurable.rb:30:inmethod_missing'
>
/usr/share/foreman/vendor/ruby/1.9.1/gems/rack-1.4.7/lib/rack/builder.rb:134:in
call' > /usr/share/foreman/vendor/ruby/1.9.1/gems/rack-1.4.7/lib/rack/urlmap.rb:64:inblock in call'
>
/usr/share/foreman/vendor/ruby/1.9.1/gems/rack-1.4.7/lib/rack/urlmap.rb:49:in
each' > /usr/share/foreman/vendor/ruby/1.9.1/gems/rack-1.4.7/lib/rack/urlmap.rb:49:incall'
>
/usr/lib/ruby/vendor_ruby/phusion_passenger/rack/thread_handler_extension.rb:77:in
process_request' > /usr/lib/ruby/vendor_ruby/phusion_passenger/request_handler/thread_handler.rb:142:inaccept_and_process_next_request'
>
/usr/lib/ruby/vendor_ruby/phusion_passenger/request_handler/thread_handler.rb:110:in
main_loop' > /usr/lib/ruby/vendor_ruby/phusion_passenger/request_handler.rb:448:inblock (3 levels) in start_threads'
>
/usr/share/foreman/vendor/ruby/1.9.1/gems/logging-2.0.0/lib/logging/diagnostic_context.rb:448:in
call' > /usr/share/foreman/vendor/ruby/1.9.1/gems/logging-2.0.0/lib/logging/diagnostic_context.rb:448:inblock in create_with_logging_context'

> Here's the full stack trace from the log file

Can you share the earlier part of the request, showing the parameters
you're passing? It's hard to tell without figuring out the Python code.

My only guess is that you might want to pass
operatingsystems_ids:[1,2,3] instead of "operatingsystems".

··· On 01/10/15 22:02, Ryan Dyer wrote:

2015-10-01T16:01:13 [app] [W] Action failed

NoMethodError: undefined method merge!' for nil:NilClass /usr/share/foreman/app/controllers/api/v2/base_controller.rb:119:inblock in append_array_of_ids’
/usr/share/foreman/app/controllers/api/v2/base_controller.rb:114:in
each' /usr/share/foreman/app/controllers/api/v2/base_controller.rb:114:inappend_array_of_ids’
/usr/share/foreman/app/controllers/api/v2/base_controller.rb:132:in
`setup_has_many_params’


Dominic Cleal
dominic@cleal.org

Entire python script attached. It expects to find two OS within Foreman.
One defined with description "OS <version>", the other "OS <version> -
RC". The non-RC one will have provisioning templates associated to it.
The goal of the script is to determine those templates and associate the RC
one to those templates. This is part of a larger script which attempts to
clone one OS into an RC OS and make appropriate changes where necessary.

issue.py (2.71 KB)

··· On Tuesday, October 6, 2015 at 2:02:58 AM UTC-5, Dominic Cleal wrote: > > On 01/10/15 22:02, Ryan Dyer wrote: > > Here's the full stack trace from the log file > > Can you share the earlier part of the request, showing the parameters > you're passing? It's hard to tell without figuring out the Python code. > > My only guess is that you might want to pass > operatingsystems_ids:[1,2,3] instead of "operatingsystems". > > > 2015-10-01T16:01:13 [app] [W] Action failed > > > NoMethodError: undefined method `merge!' for nil:NilClass > > > /usr/share/foreman/app/controllers/api/v2/base_controller.rb:119:in > > `block in append_array_of_ids' > > > /usr/share/foreman/app/controllers/api/v2/base_controller.rb:114:in > > `each' > > > /usr/share/foreman/app/controllers/api/v2/base_controller.rb:114:in > > `append_array_of_ids' > > > /usr/share/foreman/app/controllers/api/v2/base_controller.rb:132:in > > `setup_has_many_params' > > > -- > Dominic Cleal > dom...@cleal.org >

>
> My only guess is that you might want to pass operatingsystems_ids:[1,2,3]
> instead of "operatingsystems".
>

This is where I think there may be an issue. The API documentation says it
should be operatingsystem_ids, but when you output a config template, the
field is operatingsystems. All of the other fields in a config template
match in both the output (GET config_template) and the API doc except for
this one.

I modified my code to create the array of os_ids and this seems to work. I
think something should be done in regards to either the PUT or the GET so
that the fields match. You do not GET an os_id array from a
config_template, but are expected to PUT one in order to modify the
operatingsystems field you do GET.

··· On Tuesday, October 6, 2015 at 2:00:38 PM UTC-5, Ryan Dyer wrote:

My only guess is that you might want to pass operatingsystems_ids:[1,2,3]

instead of “operatingsystems”.

This is where I think there may be an issue. The API documentation says
it should be operatingsystem_ids, but when you output a config template,
the field is operatingsystems. All of the other fields in a config
template match in both the output (GET config_template) and the API doc
except for this one.

I take it back, it's not throwing any errors now, but it's not actually
associating the OS to the template. The log is showing that it is
successfully doing so but the template in question still only has a single
OS associated to it.

2015-10-06T14:29:39 [app] [I] Started PUT "/api/config_templates/279" for
10.10.10.12 at 2015-10-06 14:29:39 -0500
2015-10-06T14:29:39 [app] [I] Processing by
Api::V2::ConfigTemplatesController#update as JSON
2015-10-06T14:29:39 [app] [I] Parameters: {"operatingsystem_ids"=>[181,
164], "apiv"=>"v2", "id"=>"279",
"provisioning_template"=>{"operatingsystem_ids"=>[181, 164]}}
2015-10-06T14:29:39 [app] [I] Authorized user api(API User)
2015-10-06T14:29:39 [app] [I] Completed 200 OK in 48.0ms (Views: 1.1ms |
ActiveRecord: 0.0ms)

··· On Tuesday, October 6, 2015 at 2:13:08 PM UTC-5, Ryan Dyer wrote:

I modified my code to create the array of os_ids and this seems to work.
I think something should be done in regards to either the PUT or the GET so
that the fields match. You do not GET an os_id array from a
config_template, but are expected to PUT one in order to modify the
operatingsystems field you do GET.

Looks like a new bug here related to the renaming of templates.

Internally the controller copies the supplied attributes into a further
hash inside the parameters (wrapping), here it's called
"provisioning_template" but the controller expects it to be called
"config_template". Please do file this issue, it's a regression we need
to fix.

You may be able to workaround it by passing a nested hash into the
request body:

{"config_template":{"operatingsystem_ids":[181,164]}}

Cheers,

··· On 06/10/15 20:31, Ryan Dyer wrote: > I take it back, it's not throwing any errors now, but it's not actually > associating the OS to the template. The log is showing that it is > successfully doing so but the template in question still only has a > single OS associated to it. > > 2015-10-06T14:29:39 [app] [I] Started PUT "/api/config_templates/279" > for 10.10.10.12 at 2015-10-06 14:29:39 -0500 > 2015-10-06T14:29:39 [app] [I] Processing by > Api::V2::ConfigTemplatesController#update as JSON > 2015-10-06T14:29:39 [app] [I] Parameters: > {"operatingsystem_ids"=>[181, 164], "apiv"=>"v2", "id"=>"279", > "provisioning_template"=>{"operatingsystem_ids"=>[181, 164]}}


Dominic Cleal
dominic@cleal.org

Nesting the os_id did work for associating the OS to the template. Thanks

I posted issue Bug #12089: Unable to Associate OS to Provisioning Templates via API - Foreman to track this
issue.

Also, can you enlighten on what API to use to "select" the template in the
OS? The docs show a PUT of both operatingsystem[config_template_ids] and
operatingsystem[provisioning_template_ids]. Though much like
config_templates, neither of these exist with a GET. Also how would either
of these know which template kind the to associate to. Does it just auto
determine which one needs to go where? The API in 1.6 required you to send
up the entire hash for config_templates which would include the kind of
template as well as its id.

Thanks again,
Ryan

Looks like a new bug here related to the renaming of templates.

··· > > Internally the controller copies the supplied attributes into a further > hash inside the parameters (wrapping), here it's called > "provisioning_template" but the controller expects it to be called > "config_template". Please do file this issue, it's a regression we need > to fix. > > You may be able to workaround it by passing a nested hash into the > request body: > > {"config_template":{"operatingsystem_ids":[181,164]}} > > Cheers, > > -- > Dominic Cleal > dom...@cleal.org >

Nm, found POST
/api/operatingsystems/:operatingsystem_id/os_default_templates

You guys do not make this easy.

··· > Also, can you enlighten on what API to use to "select" the template in the > OS? The docs show a PUT of both operatingsystem[config_template_ids] and > operatingsystem[provisioning_template_ids]. Though much like > config_templates, neither of these exist with a GET. Also how would either > of these know which template kind the to associate to. Does it just auto > determine which one needs to go where? The API in 1.6 required you to send > up the entire hash for config_templates which would include the kind of > template as well as its id. > >