Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Manual provisioning of existing machines does not work #106

Closed
ThomasBri opened this issue Apr 10, 2017 · 9 comments
Closed

Manual provisioning of existing machines does not work #106

ThomasBri opened this issue Apr 10, 2017 · 9 comments
Assignees

Comments

@ThomasBri
Copy link

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"')

@tvansteenburgh
Copy link
Contributor

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"')

@ThomasBri
Copy link
Author

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
}

@pengale
Copy link
Contributor

pengale commented Apr 10, 2017

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.

@pengale
Copy link
Contributor

pengale commented Apr 10, 2017

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.

@ThomasBri
Copy link
Author

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.

@lorenzotomasini
Copy link

lorenzotomasini commented Apr 24, 2017

Hi,

We are still struggling with this issue. Is there any update?

Thanks

@jsrz
Copy link

jsrz commented Dec 13, 2017

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.

@AdamIsrael AdamIsrael self-assigned this Jun 5, 2018
@AdamIsrael
Copy link
Contributor

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:

  • ssh to the target machine
  • add ubuntu user, if it doesn't already exist
  • add ubuntu user to sudoers, with passwordless sudo access
  • add authorized keys to ubuntu user
  • detect hardware and series
  • check if machine is provisioned

Only after all that is done is the call to AddMachinesV2 called.

@johnsca
Copy link
Contributor

johnsca commented Sep 17, 2018

This was fixed in #240

@johnsca johnsca closed this as completed Sep 17, 2018
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

7 participants