lxc / ruby-lxc

ruby bindings for liblxc
https://linuxcontainers.org/lxc
GNU Lesser General Public License v2.1
133 stars 29 forks source link

#attach, #run_command and stdout #26

Closed tripledes closed 10 years ago

tripledes commented 10 years ago

Hi,

first of all I'd like to say I'm not 100% sure whether this is a bug...so please let me know, it might just be a misunderstanding.

I'm working on a puppet module for full lxc management and while working on configuring interfaces I hit an issue (for my purpose) where #config_item('lxc.network.0.ipv4') returns the IP without the mask (i.e. 10.0.3.2 instead of 10.0.3.2/24) which is what I need on the puppet side.

To overcome this result, I thought I might give #attach and #run_command a try I saw I could pass :stdout to the opts hash to redirect the output of 'ip addr show dev'. So far I have only been able to do it with a file, I tried pipes and a StringIO object, no luck.

While using pipes or StringIO objects, I've got what I believe it's the issue which I'm reporting, the command is being executed on the host instead of in the container... :question:

The code is here: https://gist.github.com/tripledes/ae4509dbd30bc6826070

What I get when executing it is the following:

1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default 
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
    inet6 ::1/128 scope host 
       valid_lft forever preferred_lft forever
2: enp0s3: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP group default qlen 1000
    link/ether 08:00:27:88:55:fc brd ff:ff:ff:ff:ff:ff
    inet 10.0.2.15/24 brd 10.0.2.255 scope global dynamic enp0s3
       valid_lft 57426sec preferred_lft 57426sec
    inet6 fe80::a00:27ff:fe88:55fc/64 scope link 
       valid_lft forever preferred_lft forever
3: lxcbr0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default 
    link/ether fe:95:05:1d:e8:04 brd ff:ff:ff:ff:ff:ff
    inet 10.0.3.1/24 scope global lxcbr0
       valid_lft forever preferred_lft forever
    inet6 fe80::fc95:5ff:fe1d:e804/64 scope link 
       valid_lft forever preferred_lft forever
5: vethA2MDOD: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast master lxcbr0 state UP group default qlen 1000
    link/ether fe:95:05:1d:e8:04 brd ff:ff:ff:ff:ff:ff
    inet6 fe80::fc95:5ff:fe1d:e804/64 scope link 
       valid_lft forever preferred_lft forever

I've tested the same script in both Archlinux and Ubuntu 14.04 with ruby-lxc 1.2.0 and latest liblxc available in both distros.

Any clue what's going on?

And any tips on how to capture the output without the need of temporary files would also be much appreciated.

ranjib commented 10 years ago

you have to add wait: true to tell attach wait for the command to finish

out = LXC::Container.new('foo').attach(wait: true){`ip addr show`}

I am not sure how stringio can be used .. i think attach is expecting a file descriptor, i generally achieve similar thing using a pipe regards ranjib

andrenth commented 10 years ago

Ranjib is right on both accounts. The LXC API needs file descriptors for the attach options.

Maybe I should make the check for the std{in,out,err} options more strict. Currently I check if the object responds to sysread and syswrite. I guess a better check would be the equivalent of obj.respond_to?(:fileno) && !obj.fileno.nil?.

Cheers, Andre

andrenth commented 10 years ago

By the way, LXC.run_command is pretty useless isn't it? system or backticks inside an attach block will work just fine.

tripledes commented 10 years ago

Thanks for the help guys, I managed to get it working having a look to @ranjib's lxc-extra...so, using pipes, I obviously was doing something wrong.

I figured it out that LXC API expects file descriptors, when I got it to work with a file I had a quick look at the attach.c file and saw the dup2() call...anyway, seems a bit tricky using the opts hash.

Now, yes LXC.run_command looks a bit useless unless there's a way to capture its output...I cannot imagine many people just wanting to see its output on the screen, IMHO it would be much more powerful.

BTW, doh! (to me)...I should have thought of using backticks or system, but I was assuming there was some namespace magic going on there...anyway, glad to have it working :+1:

@andrenth I also believe there should be more strict checks on std* options as it might lead to unexpected (or even worse) results...I was wondering should it be done on the ruby side or in the liblxc side?

On the other hand, what do you think about config_item('lxc.network.index.ipv4') ? I mean, I expected it to return the value written in the configuration file, which in my case is xx.xx.xx.xx/24, but it only returns the IP part of it....having to use attach to get the complete value seems a bit overkill. I presume it has nothing to do with ruby-lxc, it is something coming from liblxc, isn't it? If so, I might consider to open an issue as I believe it would be more consistent returning the value from the config file.

andrenth commented 10 years ago

I've just commited a change that checks if the std{in,out,err} objects have an associated file descriptor. I've also made the error messages better, so your code would have generated an exception with an error like 'attach': stdout object must have a file descriptor (ArgumentError).

WRT config_item('lxc.network.index.ipv4'), the results come from liblxc. The code even parses the netmask correctly, but they only use it to calculate the broadcast address if it's not given in the container configuration. The code is in src/lxc/confile.c in the config_network_ipv4() function in the LXC repository, if you want to have a look at it and maybe submit a patch.

In the process I found a bug in liblxc's IPv6 parsing (config_item('lxc.network.index.ipv6') currently returns garbage). I sent them a patch to fix this.

tripledes commented 10 years ago

Cool, thanks a lot.

I'll have a look to confile.c and see if I'm able to produce a patch.

Closing the issue :+1: