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

Multiple Machine Configs #3

Open
wrathematics opened this issue Jan 10, 2017 · 12 comments
Open

Multiple Machine Configs #3

wrathematics opened this issue Jan 10, 2017 · 12 comments

Comments

@wrathematics
Copy link
Contributor

wrathematics commented Jan 10, 2017

I was working on something like this a year ago, but the pbdRPC design is so radically different, it wouldn't be easy to integrate. Basically what I'd like to have is an easy way to handle multiple machine configurations. What I want to be able to do is

rpc(cmd="uname", machine=newton)
rpc(cmd="uname", machine=beacon)

etc. I actually think it would be worth changing the current interface for something like this, since in the long run it would be much simpler.

In my original plan, machine configs were S4 objects, but that's not really necessary (and I'm not sure I would do it that way again anyway). But I think all of the .pbd_env$RPC_LI$ objects could be moved to a machine "object" (maybe S3/attribute just for error checking). Maybe something like:

machine <- function(user, hostname, args="", pport=22, priv.key="~/.ssh/id_rsa", priv.key.ppk=.pbd_env$RPC.LI$priv.key.ppk)
{
  m <- list(args=args, pport=pport, user=user, hostname=hostname, priv.key=priv.key, priv.key.ppk=priv.key.ppk)
  class(m) <- "remote_machine"
  return(m)
}

print.remote_machine <- function(x, ...)
{
  cat(paste0("Machine config for ", x$user, "@", x$hostname, "\n"))
  cat(paste0("    pport = ", x$pport, "\n"))
  cat(paste0("    args = \"", x$args, "\"\n"))
  cat(paste0("    priv.key = ", x$priv.key, "\n"))
  cat(paste0("    priv.key.ppk = ", x$priv.key.ppk, "\n"))
}

Then you would be able to just do:

myvm <- machine("wrathematics", "192.168.0.10")
rpc("uname", myvm)

There's nothing high minded here, but I really think this way of organization is worth considering.

@wrathematics
Copy link
Contributor Author

It might be cute to have a store_machine() function that just wraps something like:

store_machine <- function(machine)
{
  nm <- deparse(substitute(machine))
  dump(nm, "~/.Rprofile", append=TRUE)
  invisible()
}

remove_machine() would be much harder...

@wrathematics
Copy link
Contributor Author

Come to think of it, a ~/.ssh/config parser would be interesting as well. Like

my_machines <- read_ssh_config("~/.ssh/config")

Maybe something like this:

# internal
parse_ssh_config <- function(lines)
{
  errmsg <- "malformed ssh config file"
  
  lines <- gsub(lines, pattern="\\s+", replacement=" ")
  flags <- strsplit(lines, split=" ")
  names <- sapply(flags, `[`, 1)
  
  hostname <- grep(names, pattern="^HostName", ignore.case=TRUE, perl=TRUE)
  if (length(hostname) == 0)
    stop(errmsg)
  
  host <- grep(names, pattern="^Host", ignore.case=TRUE, perl=TRUE)
  host <- which(host != hostname)
  if (length(hostname) == 0)
    stop(errmsg)
  
  user <- grep(names, pattern="^User", ignore.case=TRUE, perl=TRUE)
  port <- grep(names, pattern="^Port", ignore.case=TRUE, perl=TRUE)
  
  get_args <- function(x) paste0("-o ", x[1], "=", x[2])
  flags_other <- flags[-c(hostname, host, user, port)]
  if (length(flags_other) > 0)
    args <- paste(sapply(flags_other, get_args), collapse=" ")
  else
    args <- ""
  
  
  hostname <- flags[[hostname]][2]
  host <- flags[[host]][2]
  
  if (length(user) == 0)
    user <- system("whoami")
  else
    user <- flags[[user]][2]
  
  if (length(port) == 0)
    port <- 22
  else
    port <- flags[[port]][2]
  
  list(host=host, list(user=user, hostname=hostname, pport=port, args=args))
}

#' @export
read_ssh_config <- function(file="~/.ssh/config")
{
  sshconf <- readLines(file)
  breaks <- c(0, which(sshconf == ""))
  
  # lines separated by blanks; prune multi-breaks
  lines <- lapply(2:length(breaks), function(n) sshconf[(breaks[n-1]+1):(breaks[n]-1)])
  lines <- lines[which(sapply(sapply(lines, nchar), sum) > 0)]
  
  machines_parsed <- lapply(lines, parse_ssh_config)
  names <- sapply(machines_parsed, `[`, 1)
  machines <- sapply(machines_parsed, `[`, 2)
  
  machines <- lapply(machines, function(m) machine(m$user, m$hostname, pport=m$pport, args=m$args))
  names(machines) <- names
  return(machines)
}



m <- read_ssh_config()
m

This isn't perfect (didn't bother with LocalForward and RemoteForward ssh config options, which are a little different), but it's probably good enough for most people...

@wrathematics
Copy link
Contributor Author

If you agree with the initial post (changing to machine configs), I can submit a PR. We can talk more at length about the other stuff later.

@snoweye
Copy link
Owner

snoweye commented Jan 12, 2017

Just do it. I need to buy time. Please

@wrathematics
Copy link
Contributor Author

First post implemented in PR. Second two are the wrong approach; I have a better idea.

Closing issue.

@snoweye
Copy link
Owner

snoweye commented Jan 13, 2017

I still think that rpc(cmd = ... , machine = ...) may better because most people don't need to deal with multiple machines at the same time, but need to send multiple cmd to a single machine several times ...

How about add another functions for this feature, mpc for machine procedure calls?

@snoweye snoweye reopened this Jan 13, 2017
@snoweye
Copy link
Owner

snoweye commented Jan 13, 2017

ssh config is only for server not client, no?

plink does not use it, either.

@wrathematics
Copy link
Contributor Author

You're probably right about multiple calls on one machine being more likely than calls to multiple machines in a session, but I still think the machine configuration is the way to go. Having all of the .pbd_opt$RPC.LI$... arguments in every function call is hard to read. Anyone who looks at the API will be very confused. It's also much easier to change and maintain the code when the option settings are only in one place.

This system also allows for lookups with ssh config files (which yes, is ssh only, but valuable for server and client). So you can do:

m <- machine("myaws")
rpc(myaws, m)

Where the string "myaws" is the hostname in my ssh config file. So I don't have to repeat the ip, port, user name, ... . It looks all of that up for me. I already have this working locally and it's awesome.

Maybe machine() could automatically set the last called object to a .pbd_opt$RPC.LI$machine default or something. I don't know.

@snoweye
Copy link
Owner

snoweye commented Jan 14, 2017

I goal is simply aiming for most of normal users, so that they only need two: rpcopt_set(...) once then rpc(cmd) everywhere as simple as in the example. The first is similar to give ssh or telnet some information to login in, then the second is similar to have a terminal to type any commands.

I don't expect anyone to read those of .pbd_opt, but they should be as informative as the man page whoever want to read.

I think machine is good. Either rpc(cmd, m) or rpc(cmd) is more like a function call to do some cmd, but not rpc(m1, cmd) and rpc(m2, cmd). Both are ok, but I don't know which is better.

I do reserve for more than config, even for searching ssh and plink in system or internal, Just no time to implement.

@snoweye
Copy link
Owner

snoweye commented Jan 15, 2017

exe.type is for local not remote machine. This should be determined by rpc() not machine().

Don't determine the user name from local machine. It has to be given by users or ssh config for the remote machine.

@wrathematics
Copy link
Contributor Author

I know that exec.type is for local machines, but the machine configuration arguments are dependent whether it's an ssh or putty type connection.

User name doesn't have to be given. It can be inferred with 100% accuracy from ssh config if using ssh and the hostname is there. If not, it defaults to guessing with the machine user name. Anyone can still manually provide the user name though.

I'll send another PR tonight.

@snoweye
Copy link
Owner

snoweye commented Jan 15, 2017

OK. I am find with both now. machine can be both local and remote.
I think rpc(m1, cmd) or rpc(cmd, m1) are also ok. No significant difference.
I add a section regarding machine.

I meant don't guess from windows it does not make sense. win and *nix don't talk.

snoweye added a commit that referenced this issue Aug 29, 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

2 participants