Does Get-NetIPConfiguration return interfaces in binding order? - powershell

I noticed that
(Get-NetIPConfiguration).InterfaceIndex
always seems to return the index's in interface binding order for me. is this the way its suppose to function, always returning in interface binding order or it is just a fluke that it always returns in interface binding order for me in this case?

You could try
(Get-NetIPConfiguration).InterfaceIndex | Sort [int]

ok, got this figured out. Starting with windows 10 the preferred IP Address enabled (either IPv4 or IPv6) interface is the one with the lowest sum of the IPv4 (or IPv4) route metric and the interface metric (route metric + if metric). This may not be the one with the default route 0.0.0.0/0 depending on if you have multiple interfaces set up or not with multiple default routes. That interface will bind to the one with the next lowest lowest sum on the same route ...and so forth and so on. This also, it turns out, may not be the order of the interface GUID's in the registry value at 'HKLM:\SYSTEM\Currentcontrolset\Services\TCPIP\Linkage\bind\
But...how they appear in a list, including other methods, is determined just by the interface metric. So in Windows 10 there is (i'm gonna call it) 'route binding order' and the list order. Although this may seem to match up sometimes because most people don't have multiple interfaces in their systems it becomes important if you are going to start playing around with setting interface and route metric to ensure the interface you want to be used is the one being used for a particular route. So you want the actual binding order and not the list order.
so, in windows 10, to get correct route binding order (vs list order) you have to examine the route table and do the 'math' for sum of route metric + interface metric to get actual binding order, and use that same 'sum' methodolgy to set up interfaces if you are going to do it yourself.
Below is an example of this method usage that i threw together quickly. I have one Windows 10 machine with two hardware interfaces, one for hard line connection and the other wifi. I also have OpenVPN with a TAP adapter installed on this system, so i need to know which hardware interface that OpenVPN was binding to through the TAP adapter. The TAP adapter is a virtual (software) interface. When the TAP is installed/connected it uses a interface metric less than 10 because windows minimum automatic interface metric is always no lower than 10 (although you can manually set it lower) , and the tap uses a route metric of 0. So the tap is going to be the lowest 'sum' Ip enabled interface in the system. So all this info and using the below code I got the hardware interface the Open/VPN/Tap is binding to (code thrown together quickly, will refine later when I have the time)..
$route = (Get-NetRoute); $rtmetric = $route.RouteMetric; $ifmetric = $route.InterfaceMetric; $index = $route.ifIndex;
$sumarray = #(); $indexarray = #(); For ($i=0; $i -lt $rtmetric.Length; $i++)
{
$sum = $rtmetric[$i] + $ifmetric[$i]
$sumarray += $sum
$indexarray += $index[$i]
}
$sumsort = $sumarray | sort -unique; [int]$sumlowest = ($sumsort | measure -Minimum).Minimum;
$pos = [Array]::indexof($sumsort,$sumlowest) + 1; $sumget = $sumsort[$pos]; $posA = [Array]::indexof($sumarray,$sumget); $ifindexbind = $indexarray[$posA];
(Get-NetAdapter | ? ifIndex -eq $ifindexbind)
so, that's the long way around it to get the route binding order vs the list order. If you do this:
(Get-NetAdapter).InstanceID
you will get the binding in list order as they are in the registry key starting from the bottom and working your way up. But.... if you do this
(Get-NetIPConfiguration).NetAdapter.InstanceID
you will notice that as interfaces are disabled or enable the order given will also change, and that's because '(Get-NetIPConfiguration).NetAdapter.InstanceID' is giving the route binding order and not the registry list order. Disabling an interface removes it from the route table but just disabling an interface does not remove its binding to another interface but the priority changes so the previously bound interface now becomes the preferred interface in the route so moves to the top. When that disabled interface with the binding to the enabled interface, is enabled again it takes it place in the route binding on top. The one on top will be the preferred adapter (the one with the lowest 'sum' value), the one under that will be the 'next prefered' (the one with the next lowest 'sum' value) and if on the same route is the interface bound to one above it, thus binding order. So using (Get-NetIPConfiguration).NetAdapter.InstanceID the needed route binding order for enabled and connected interfaces can be had without resorting to reading the registry.

Related

VM Shutdown Order based on string in notes

I am trying to find the best way to use the notes field to set a shutdown order of virtual machines. At the moment, I have a note in my VM's as "Startup: X" where X is the number I want to use for startup, but the reverse being what I want for shutdown. The goal is to put this into a workflow so that machines complete their startup/shutdown before the next machine comes up.
Currently, I can get the info from the VMs by doing:
Get-VM | Where-Object {$_.Notes -contains "Startup:"} | Select-Object name,notes
It's my understanding that I want to put this into an object, then loop through that object based on the value after the ":" (e.g. 1, 2, 3, 4, 5, etc.) in either ascending or descending order. However, I'm not sure if that's the best method to work on this or not. All I know is that I want to look for the Startup: line in the VM Notes field, then use that alone to determine order. Some VMs might have other notes in them that I want to ignore, so I only want the Startup: string to show.
Could anyone give me some assistance? Or, let me know if I'm on the wrong path?
I have tried using multiple methods to parse, but I keep getting null errors that I cannot figure out. I'm fairly new to using PS in this way, so that doesn't help.

Pulum DigitalOcean: use outputs

I want to create some servers on DigitalOcean using Pulumi. I have the following code:
for i in range(0, amount):
name = f"droplet-{i+1}"
droplet = digitalocean.Droplet(
name,
image=_image,
region=_region,
size=_size,
)
pulumi.export(f"droplet-ip-{i+1}", droplet.ipv4_address)
This is correctly outputting the IP address of the servers on the console.
However I would like to use the IP addresses elsewhere in my Python script. Therefor I had added the droplets to a list as follows:
droplets = []
for i in range(0, amount):
name = f"droplet-{i+1}"
droplet = digitalocean.Droplet(
name,
image=_image,
region=_region,
size=_size,
)
pulumi.export(f"droplet-ip-{i+1}", droplet.ipv4_address)
droplets.append(droplet)
to then loop over the droplets as follows:
for droplet in droplets:
print(droplet.ipv4_address)
In the Pulumi output, I see the following:
Diagnostics:
pulumi:pulumi:Stack (Pulumi_DigitalOcean-dev):
<pulumi.output.Output object at 0x105086b50>
<pulumi.output.Output object at 0x1050a5ac0>
I realize that while the droplets are still being created, the IP address is unknown but I'm adding the droplets to the list after the creation.
Is there a way to know the IP addresses at some point so it can be used elsewhere in the Python script.
The short answer is that because these values are Outputs, if you want the strings, you'll need to use .apply:
https://www.pulumi.com/docs/intro/concepts/inputs-outputs/#apply
To access the raw value of an output and transform that value into a new value, use apply. This method accepts a callback that will be invoked with the raw value, once that value is available.
You can print these IPs by iterating over the list and calling the apply method on the ipv4_address output value:
...
pulumi.export(f"droplet-ip-{i+1}", droplet.ipv4_address)
droplets.append(droplet)
...
for droplet in droplets:
droplet.ipv4_address.apply(lambda addr: print(addr))
$ pulumi up
...
Diagnostics:
pulumi:pulumi:Stack (so-71888481-dev):
143.110.157.64
137.184.92.205
Outputs:
droplet-ip-1: "137.184.92.205"
droplet-ip-2: "143.110.157.64"
Depending on how you plan to use these strings in your program, this particular may may not be perfect, but in general, if you want the unwrapped value of pulumi.Output, you'll need to use .apply().
The pulumi.Output.all() also comes in handy if you want to wait for several output values to resolve before using them:
https://www.pulumi.com/docs/intro/concepts/inputs-outputs/#all
If you have multiple outputs and need to join them, the all function acts like an apply over many resources. This function joins over an entire list of outputs. It waits for all of them to become available and then provides them to the supplied callback.
Hope that helps!

How to make Windows' Bonjour resolve foo.bar.local subdomains created by Avahi

Why can't Windows' Bonjour (the Apple one) automatically resolve foo.bar.local, when Ubuntu and macOS can?
foo.local instead is resolved without issues by every OS.
Here's my avahi-daemon.conf:
[server]
host-name=foo
domain-name=bar.local
...
This discussion mentions that Windows' Bonjour implementation does not support aliases, is this the culprit?
How does this tool differ from my solution?
EDIT: I don't want to set an alias. foo.bar.local is different from bar.local.
I just want to have different hostnames under the same "domain".
For example, foo.bar.local is 192.168.0.8 while foo1.bar.local is 192.168.0.9.
I won't have foo.local, bar.local and foo.bar.local all in the same network. I will use foo.bar.local, with only foo varying (*.bar.local)
From my current findings, this seems to be intentional. Excerpt from the source code (mDNSResponder-878.30.4, function NSPLookupServiceBegin in mdnsNSP.c):
// <rdar://problem/4050633>
// Don't resolve multi-label name
// <rdar://problem/5914160> Eliminate use of GetNextLabel in mdnsNSP
// Add checks for GetNextLabel returning NULL, individual labels being greater than
// 64 bytes, and the number of labels being greater than MAX_LABELS
replyDomain = translated;
while (replyDomain && *replyDomain && labels < MAX_LABELS)
{
label[labels++] = replyDomain;
replyDomain = GetNextLabel(replyDomain, text);
}
require_action( labels == 2, exit, err = WSASERVICE_NOT_FOUND );
It returns an error if the name has more than two labels, as in the case of foo.bar.local.
In my tests, I just removed the last line. With the new build, it resolved names with multiple labels successfully. I did not yet encounter any side-effects so far.
Has anybody an idea about the intention behind not resolving multi-label names?

Obtaining Name of Simscape Physical Port

I am working on a model generation script to automatically generate Simscape models from a library of components with varying ports. Due to the vast number of ports that need to be connected across the model, I am looking for a good way to set up which ports need to be connected to each other. The best solution I have come up with so far is to name each port with a unique tag that indicates what other ports in the system it should be connected to in the generated model. However, I am not able to obtain the name of any physical port. It is labeled on the mask, but the 'Name' parameter always comes back empty. Here's what I've tried:
h = get_param(gcb,'PortConnectivity')
port = h(1).Type %This only returns the physical port #, not custom name
h = get_param(gcb,'PortHandles')
port = get_param(h.LConn(1),'Name') %This returns an empty cell array
Not sure where to go from here. Any ideas on how to solve this? Thanks!
You can use:
name = get_param(gcb, 'Name');
to get the port name. A general tip for finding the right block property, run:
get(get_param(gcb, 'object'))
this will display all block properties and their values.

Traversing DOM nodes in CKEditor-4

We have a bug at this CKEditor plugin, and a generic solution is like to this, applying a generic string-filter only to terminal text nodes of the DOM.
QUESTION: how (need code example) to traverse a selection node (editor.getSelection()) by CKEditor-4 tools, like CKEDITOR.dom.range?
First step will be to get ranges from current selection. To do this just call:
var ranges = editor.getSelection().getRanges();
This gives you an array of ranges, because theoretically (and this theory is proved only by Firefox) one selection can contain many ranges. But in 99% of real world cases you can just handle the first one and ignore other. So you've got range.
Now, the easiest way to iterate over each node in this range is to use CKEDITOR.dom.walker. Use it for example this way:
var walker = new CKEDITOR.dom.walker( range ),
node;
while ( ( node = walker.next() ) ) {
// Log values of every text node in range.
console.log( node.type == CKEDITOR.NODE_TEXT && node.getText() );
}
However, there's a problem with text nodes at the range's boundaries. Consider following range:
<p>[foo<i>bar</i>bo]m</p>
This will log: foo, bar, and bom. Yes - entire bom, because it is a single node and walker does not modify DOM (there's a bug in documentation).
Therefore you should check every node you want to transform if it equals range's startContainer or endContainer and if yes - transform only that part of it which is inside range.
Note: I don't know walker's internals and I'm not sure whether you can modify DOM while iterating over it. I'd recommend first caching nodes and then making changes.