Closed ThomasBri closed 6 years ago
Here's the code that was used:
async def add_machine(ip, model):
ssh_ubuntu__format = 'ssh:ubuntu@{}'.format(ip)
m = await model.add_machine(spec=ssh_ubuntu__format)
return m
async def test():
model = Model()
await model.connect('xx.xx.xx.xx:17070', 'd06c23f7-aacf-44c8-8819-166b9baf3860', 'xxxx', 'xxxxx')
print('Start adding machine')
print(await add_machine('xx.xx.xx.xx', model))
print('Finished')
await model.disconnect()
And here's the log:
DEBUG:websockets.protocol:client >> Frame(fin=True, opcode=1, data=b'{\n "request-id": 4,\n "request": "AddMachines",\n "params": {\n "params": [\n {\n "placement": {\n "scope": "ssh",\n "directive": "ubuntu@192.168.77.242"\n },\n "parent-id": null,\n "addresses": [],\n "disks": [],\n "jobs": [\n "JobHostUnits"\n ],\n "hardware-characteristics": null,\n "nonce": null,\n "container-type": null,\n "constraints": null,\n "series": null,\n "instance-id": null\n }\n ]\n },\n "version": 1,\n "type": "Client"\n}')
WARNING:asyncio:Executing <Task pending coro=<test() running at /opt/openbaton/jujunew/test/new.py:178> wait_for=<Future pending cb=[Task._wakeup()] created at /usr/lib/python3.5/asyncio/base_events.py:252> cb=[_run_until_complete_cb() at /usr/lib/python3.5/asyncio/base_events.py:164] created at /usr/lib/python3.5/asyncio/base_events.py:367> took 3.741 seconds
DEBUG:asyncio:poll took 0.051 ms: 1 events
DEBUG:websockets.protocol:client << Frame(fin=True, opcode=1, data=b'{"request-id":4,"response":{"machines":[{"machine":"","error":{"message":"invalid model name \\"ssh\\"","code":""}}]}}\n')
Traceback (most recent call last):
File "/usr/local/lib/pycharm-2016.3.2/helpers/pydev/pydevd.py", line 1596, in <module>
globals = debugger.run(setup['file'], None, None, is_module)
File "/usr/local/lib/pycharm-2016.3.2/helpers/pydev/pydevd.py", line 974, in run
pydev_imports.execfile(file, globals, locals) # execute the script
File "/usr/local/lib/pycharm-2016.3.2/helpers/pydev/_pydev_imps/_pydev_execfile.py", line 18, in execfile
exec(compile(contents+"\n", file, 'exec'), glob, loc)
File "/opt/openbaton/jujunew/test/new.py", line 218, in <module>
exec_and_wait(test())
File "/opt/openbaton/jujunew/test/new.py", line 215, in exec_and_wait
return loop.run_until_complete(routine)
File "/usr/lib/python3.5/asyncio/base_events.py", line 387, in run_until_complete
return future.result()
File "/usr/lib/python3.5/asyncio/futures.py", line 274, in result
raise self._exception
File "/usr/lib/python3.5/asyncio/tasks.py", line 239, in _step
result = coro.send(None)
File "/opt/openbaton/jujunew/test/new.py", line 178, in test
print(await add_machine('192.168.77.242', model))
File "/usr/lib/python3.5/asyncio/coroutines.py", line 105, in __next__
return self.gen.send(None)
File "/opt/openbaton/jujunew/test/new.py", line 168, in add_machine
m = await model.add_machine(spec=ssh_ubuntu__format)
File "/usr/lib/python3.5/asyncio/coroutines.py", line 105, in __next__
return self.gen.send(None)
File "/home/tbr/.virtualenvs/jujuvnfm/lib/python3.5/site-packages/juju/model.py", line 795, in add_machine
raise ValueError("Error adding machine: %s", error.message)
ValueError: ('Error adding machine: %s', 'invalid model name "ssh"')
The actual ws json request is:
{
"request": "AddMachines",
"type": "Client",
"params": {
"params": [
{
"jobs": [
"JobHostUnits"
],
"placement": {
"directive": "ubuntu@xx.xx.xx.xx",
"scope": "ssh"
},
"nonce": null,
"disks": [],
"parent-id": null,
"container-type": null,
"addresses": [],
"hardware-characteristics": null,
"instance-id": null,
"series": null,
"constraints": null
}
]
},
"version": 1,
"request-id": 4
}
It sounds like the parse function in juju/placement.py is doing the wrong thing here. I'm assuming that instead of this:
"placement": {
"directive": "ubuntu@xx.xx.xx.xx",
"scope": "ssh"
},
We want this:
"placement": {
"directive": "ssh:ubuntu@xx.xx.xx.xx",
"scope": "<some valid scope, or possibly null>"
},
parse.py is basically one big hack to get around the fact that the planner doesn't give us placement directives that we can actually pass back to it, so it wouldn't be too terrible if we added one more hack to it, to make it treat "ssh:..." as a special case.
Follow-up: instead of hacking parse.py, you could also pass in an instance of client.Placement to the spec arg of model.add_machine, rather than the bare "ssh:..." string. That will cause parse.py to skip over it entirely.
It seems that changing the placement is not enough here. Variations of something like
placement = Placement(scope='model-uuid', directive='ssh:ubuntu@{}'.format(ip))
await model.add_machine(spec=placement)
did not work for me. According to Juju's log when using the CLI, the request for manual provisioning looks like this:
{
"request-id":2,
"type":"Client",
"version":1,
"request":"AddMachinesV2",
"params":{
"params":[
{
"series":"trusty",
"constraints":{ },
"jobs":[
"JobHostUnits"
],
"parent-id":"",
"container-type":"",
"instance-id":"manual:xx.xx.xx.xx",
"nonce":"manual:xx.xx.xx.xx:40d94bb5-6271-243b-81c2-5bf85671bf86",
"hardware-characteristics":{
"arch":"amd64",
"mem":993,
"cpu-cores":1
},
"addresses":[
{
"value":"xx.xx.xx.xx",
"type":"ipv4",
"scope":"public"
}
]
}
]
}
}
I tried using the client module's AddMachinesV2 method and passing these parameters, which resulted in the same request to the API. However, manual provisioning seems to require more than that since the machine added by this call remained in the pending state and there were no logs created on the actual virtual machine as it was the case when using the CLI.
It would be very helpful to know the exact steps and API calls executed in the manual provisioning procedure.
Hi,
We are still struggling with this issue. Is there any update?
Thanks
I've come across this issue as well.
A work around for this is to use the python 'sh' module that you can install with pip. Import with "from sh import juju". Then run "juju('add-machine', ...)" in your code.
If you are having issues with the host key/finger print when calling add machine this way. You can pull it in to the host calling juju add machine by running something like: from sh import ssh ssh('-oStrictHostKeyChecking=no', user_and_machine, 'exit')
Only issue is, since StrictHostKeyChecking it turned off, you open yourself up to man in the middle shenanigans.
The core problem is that there are several steps that the equivalent Juju CLI command do, outside of the API, that we need to re-implement. These include:
Only after all that is done is the call to AddMachinesV2 called.
This was fixed in #240
Trying to add an existing machine to a model via ssh using the model.add_machine method like this:
await model.add_machine(spec='ssh:ubuntu@{}'.format(ip_address))
raises the following error:
ValueError: ('Error adding machine: %s', 'invalid model name "ssh"')