Last night while working on one of my many side projects I came across a relly weird Javascript issue. Here is an extremely simplified version of the code I was debugging:
$ cat app.js
const myLib = require('./lib.js');
var i = myLib.populateObject("Ollie", "Awesome");
console.log(i);
$ cat lib.js
var populateObject = (name, breed) => {
var obj = { name: name, breed: breed }
return
{
obj
}
}
module.exports = {
populateObject
}
When the code ran it would return undefined for the object called obj even though console.log() showed it as a valid object inside the function:
$ node app.js
undefined
A seasoned Javascript developer would look at the code above and immediately see the flaw. Being new to javascript it wasn’t immediately clear to me why this wasn’t working. So, I figured this would be as good a time as any to learn how to use the inspect debugger to toubleshoot my issue. The inspect debugger can be accessed by invoking node with the inspect option and the code to run:
$ node inspect app.js
< Debugger listening on ws://127.0.0.1:9229/8d8caf5a-07d1-4043-9418-1acc6935c973
< For help see https://nodejs.org/en/docs/inspector
< Debugger attached.
Break on start in app.js:1
> 1 (function (exports, require, module, __filename, __dirname) { const myLib = require('./lib.js');
2 var i = myLib.populateObject("Ollie", "Awesome");
3 console.log(i);
Once you are inside the debug shell you can use c to continue execution, n to step to the next line, s to step into functions, o to step out of functions and bt to get a backtrace. You can also use setBreakpoint and clearBreakpoint to set and clear breakpoints on specific lines of code and repl to interrogate objects. To see what was going on I stepped into the populateObject function, set a breakpoint on the return line and hit n to see what was run:
debug> s
break in lib.js:2
1 (function (exports, require, module, __filename, __dirname) { var populateObject = (name, breed) => {
> 2 var obj = { name: name, breed: breed }
3 return
4 {
debug> setBreakpoint(3)
1 (function (exports, require, module, __filename, __dirname) { var populateObject = (name, breed) => {
2 var obj = { name: name, breed: breed }
> 3 return
4 {
5 obj
6 }
7 }
8
debug> c
break in lib.js:3
1 (function (exports, require, module, __filename, __dirname) { var populateObject = (name, breed) => {
2 var obj = { name: name, breed: breed }
> 3 return
4 {
5 obj
debug> n
break in lib.js:3
1 (function (exports, require, module, __filename, __dirname) { var populateObject = (name, breed) => {
2 var obj = { name: name, breed: breed }
> 3 return
4 {
5 obj
debug> n
break in app.js:3
1 (function (exports, require, module, __filename, __dirname) { const myLib = require('./lib.js');
2 var i = myLib.populateObject("Ollie", "Awesome");
> 3 console.log(i);
4
5 });
The return completed but for some reason line 5 wasn’t being evaluated. Then it dawned on me! One of my Javascript instructors mentioned that semicolons were implied if they aren’t specifically specified. So my hypothesis became was there an implicit semicolon added to the return that casued it to return without any values? To see if that was the case I moved the opening brace to be on the same line as the return statement:
var populateObject = (name, breed) => {
var obj = { name: name, breed: breed }
return {
obj
}
}
module.exports = {
populateObject
}
And ran my program again. Low and behold it worked! To truly verify this was the issue I re-ran the program under inspect and compared the new output with the previous output I collected:
debug> s
break in lib.js:2
1 (function (exports, require, module, __filename, __dirname) { var populateObject = (name, breed) => {
> 2 var obj = { name: name, breed: breed }
3 return {
4 obj
debug> setBreakpoint(3)
1 (function (exports, require, module, __filename, __dirname) { var populateObject = (name, breed) => {
2 var obj = { name: name, breed: breed }
> 3 return {
4 obj
5 }
6 }
7
8 module.exports = {
debug> n
break in lib.js:3
1 (function (exports, require, module, __filename, __dirname) { var populateObject = (name, breed) => {
2 var obj = { name: name, breed: breed }
> 3 return {
4 obj
5 }
debug> n
break in lib.js:5
* 3 return {
4 obj
> 5 }
6 }
7
debug> n
break in app.js:3
1 (function (exports, require, module, __filename, __dirname) { const myLib = require('./lib.js');
2 var i = myLib.populateObject("Ollie", "Awesome");
> 3 console.log(i);
4
5 });
debug> repl
Press Ctrl + C to leave debug repl
> i
{ obj: Object }
> console.log(i)
< { obj: { name: 'Ollie', breed: 'Awesome' } }
In the output above you can see that we are now reaching line 5 in the program which wasn’t occurring before. This was a silly bug (the result of staying up WAY too late to code) but I learned a TON about the inspect interface in the process. Hopefully as I write more code and study the correct way to do things silly bugs like these will become less prevalent. Viva inspect!
While performing a routine audit of my desktop this morning I noticed that the systemd stub resolver was listening on TCP port 5355:
$ netstat -pant | grep 5355
tcp 0 0 0.0.0.0:5355 0.0.0.0:* LISTEN 2236/systemd-resolv
TCP port 5355 is used for Link-Local Multicast Name Resolution (LLMNR) which is completely unnecessary for my set up at home. So I ventured off to /etc/systemd and came across the resolved.conf file. While perusing resolved.conf(5) I came across the following two configuration directives:
LLMNR=
Takes a boolean argument or “resolve”. Controls Link-Local Multicast Name Resolution support (RFC 4794[1]) on the local host. If true, enables full LLMNR responder and resolver support. If false, disables both. If set to “resolve”, only resolution support is enabled, but responding is disabled. Note that systemd-networkd.service(8) also maintains per-link LLMNR settings. LLMNR will be enabled on a link only if the per-link and the global setting is on.
MulticastDNS=
Takes a boolean argument or “resolve”. Controls Multicast DNS support (RFC 6762[2]) on the local host. If true, enables full Multicast DNS responder and resolver support. If false, disables both. If set to “resolve”, only resolution support is enabled, but responding is disabled. Note that systemd- networkd.service(8) also maintains per-link Multicast DNS settings. Multicast DNS will be enabled on a link only if the per-link and the global setting is on.
Setting both values to no and bouncing the systemd-resolved.service service stopped systemd-resolv from listening on *:5355. If you are interested in learning more about LLMNR you can check out RFC 4795. This helped me clarify a number of questions I had about the protocol and when to use it.
Like most admins I use regular expressions pretty much every day. When I’m writing more complex expressions I’ve always wanted to visually observe what is actually happening. Well, to my surprise someone came up with an incredible online tool to do just that:
Online regular expression evaluation website
This is definitely one I will be adding to my bookmark bar!
For the past year I’ve been working quite a bit with VMWare’s vSphere virtualization suite. Managing vSphere through the web UI has been extremely frustrating though. The bugs, constant crashes and sluggishness have really impeded my ability to be productive. Being a command line user who loves to automate I decided to test out the govc command line utility to see if this would improve my experience. After a good deal of use I can affirmatively say that this gem makes a HUGE difference in productivity!
To get going with govc all you need is a Linux machine. The govc binary is opensource and the build team provides binary builds on their release page. Installing it is easy:
$ wget https://github.com/vmware/govmomi/releases/download/v0.16.0/govc_linux_amd64.gz
$ gunzip govc_linux_amd64.gz && mv govc_linux_amd64 /usr/local/bin/govc && chmod 755 /usr/local/bin/govc
If you want to live on the cutting edge you can also build govc from source:
$ mkdir govc
$ export GOPATH=/home/matty/govc
$ cd govc
$ go get -u github.com/vmware/govmomi/govc
$ cp bin/govc /usr/local/bin/govc && chmod 755 /usr/local/bin/govc
Once installed you can verify the binary works by running it with the about command:
$ govc about
Name: VMware vCenter Server
Vendor: VMware, Inc.
Version: 6.5.0
Build: 7119157
OS type: linux-x64
API type: VirtualCenter
API version: 6.5
Product ID: vpx
UUID: 93c8b2a5-3715-4a27-8a3a-4758848ddce0
To list all of the commands govc can be run without an argument:
$ govc
Usage of govc:
about
about.cert
cluster.add
cluster.change
cluster.create
datacenter.create
datacenter.info
datastore.cp
.....
To connect govc to a vCenter instance you will need to export a couple of variables (if you specify a root CA certificate bundle with the GOVC_TLS_CA_CERTS option you can leave out the first export):
$ export GOVC_INSECURE=true
$ export GOVC_URL='https://USER:PASSWORD@vcenter.prefetch.net/sdk'
$ export GOVC_DATACENTER=DC
To see the true utility of govc let me show a couple of examples. Govc takes a command as the first argument followed by one or more options. The following examples show how to list all of your vCenter objects and retrieve information about your data center:
$ govc ls
/DC/vm
/DC/network
/DC/host
/DC/datastore
$ govc datacenter.info
Name: DC
Path: /DC
Hosts: 2
Clusters: 1
Virtual Machines: 2
Networks: 7
Datastores: 6
To view all of your data stores you can use the datastore.info command:
$ govc datastore.info
Name: ds01
Path: /DC/datastore/ds01
Type: VMFS
URL: ds:///vmfs/volumes/586a9fb0-4b44f5b6-942c-001b21b97588/
Capacity: 225.2 GB
Free: 224.3 GB
To find an object you can use the find command (this is super handy):
$ govc find vm -name *kub*
vm/kub1
To get information about an ESXi host you can pass the name of the host to the host.info command:
$ govc host.info esx01.homefetch.net
Name: esx01
Path: /DC/host/ProdCluster/esx01
Manufacturer: MSI
Logical CPUs: 4 CPUs @ 3192MHz
Processor type: Intel(R) Core(TM) i5-6500 CPU @ 3.20GHz
CPU usage: 182 MHz (1.4%)
Memory: 65411MB
Memory usage: 14433 MB (22.1%)
Boot time: 2017-12-19 13:23:03.394348 +0000 UTC
State: connected
To retrieve all of the information about a VM you can use the vm.info command:
$ govc vm.info kub1
Name: kub1
Path: /DC/vm/kub1
UUID: 420fd911-0f20-0898-34ed-2b055519a54e
Guest name: Red Hat Enterprise Linux 7 (64-bit)
Memory: 4096MB
CPU: 1 vCPU(s)
Power state: poweredOn
Boot time: 2017-12-19 13:19:08.926664 +0000 UTC
IP address: 192.168.1.213
Host: esx02
Govc also provides access to the host and VM metrics. You can get a list of metrics for an object with the metric.ls command:
$ govc metric.ls vm/kub1
cpu.usagemhz.average
cpu.run.summation
cpu.overlap.summation
cpu.ready.summation
cpu.demand.average
cpu.readiness.average
cpu.entitlement.latest
To collect a sample you can use the metric.sample command:
$ while :; do govc metric.sample vm/kub1 cpu.usagemhz.average; sleep 1; done
kub1 - cpu.usagemhz.average 1777,17,8,17,11,9 MHz
kub1 0 cpu.usagemhz.average 1774,5,4,4,4,4 MHz
kub1 - cpu.usagemhz.average 1777,17,8,17,11,9 MHz
kub1 0 cpu.usagemhz.average 1774,5,4,4,4,4 MHz
In addition to the commands listed above I’ve found the following just as useful (you can get a full list of commands and their options on the govc github page):
datastore.upload - upload object to a data store
dvs.portgroup.info - get DVS port group info
events - view events
folder.create - create a folder
host.maintenance.enter - put a host into maintenance mode
host.maintenance.exit - take a host out of maintenance mode
logs - view specific logs
snapshot.create - create a snapshot
snapshot.remove - delete a snapshot
tasks - view vCenter tasks
vm.power - power a VM on and off
Huge thanks to the govc team as well as the maintainers of the govmomi library!
This has been an exciting week of code analysis and debugging. As I mentioned previously cobbler doesn’t currently have support for Fedora 27. It it turns out adding a new Operating System signature is super easy. For my specific situation I forked the cobbler git repo and copied the Fedora 26 signature line and changed the 6 to a 7. Once I updated the signature file and restarted cobblerd everything imported without issue:
$ cobbler import --name=fedora27 --arch=x86_64 --path=/mnt
task started: 2017-12-16_095536_import
task started (id=Media import, time=Sat Dec 16 09:55:36 2017)
Found a candidate signature: breed=redhat, version=rhel6
Found a candidate signature: breed=redhat, version=rhel7
Found a candidate signature: breed=redhat, version=fedora21
Found a candidate signature: breed=redhat, version=fedora20
Found a candidate signature: breed=redhat, version=fedora23
Found a candidate signature: breed=redhat, version=fedora22
Found a candidate signature: breed=redhat, version=fedora25
Found a candidate signature: breed=redhat, version=fedora24
Found a candidate signature: breed=redhat, version=fedora27
Found a matching signature: breed=redhat, version=fedora27
Adding distros from path /var/www/cobbler/ks_mirror/fedora27-x86_64:
creating new distro: fedora27-x86_64
trying symlink: /var/www/cobbler/ks_mirror/fedora27-x86_64 -> /var/www/cobbler/links/fedora27-x86_64
creating new profile: fedora27-x86_64
associating repos
checking for rsync repo(s)
checking for rhn repo(s)
checking for yum repo(s)
starting descent into /var/www/cobbler/ks_mirror/fedora27-x86_64 for fedora27-x86_64
processing repo at : /var/www/cobbler/ks_mirror/fedora27-x86_64
need to process repo/comps: /var/www/cobbler/ks_mirror/fedora27-x86_64
looking for /var/www/cobbler/ks_mirror/fedora27-x86_64/repodata/*comps*.xml
Keeping repodata as-is :/var/www/cobbler/ks_mirror/fedora27-x86_64/repodata
*** TASK COMPLETE ***
Now back to our regularly scheduled programming. Time for some eBPF testing!