I have been working with a new pediatric oncology camp that is getting started in Chicago that will be at the Ritz Carlton Hotel, Chicago called Camp Kids are Kids.
How cool is it that there will be a pediatric oncology camp in a hotel in the city.
Take a look at their website at http://www.campkidsarekids.org. They are getting ready for the camp to start in Aug 2014.
Wednesday, March 26, 2014
Monday, November 11, 2013
1 year of Honey Bees
Its been about 7 months now from when we first got our bee package.
During that time I have learned a lot about mostly the bee anatomy and parasites and diseases that affect them. I also learned how much I love watching and being around bees. I will regularly spend time standing near the hive to allow the bees to fly around me and land on me. While I have a small allergy to them I have not had a problem with this hive. The Italian honey bees that are in the hive are very calm and do not seem to have any problems when I tend to their home.
In the time that we have had the hive we have had to relocate it 2 separate times. Each move was ~5 and 20 feet from the original location. The first was due to a construction worker that was working on a house in the lot next to our house threating lawsuit because "some bees" swarmed him while he was operating the back-hoe.
Long story sort this guy was a dick! He had some incident with wasps in the past and he got all freaked out because some bees were hanging out around his equipment (obviously not the reproductive kind). Without any knowledge and without visibly watching each of the bees return to the hive on my property he assumed it was from my hive box. When I asked if he carried an EPIpen in the even he was stung he said "no". So we will just go down the path of him being a dumb ass.
"Your bees swarmed me..." - Dumb Ass (2013)
While I was not concerned about losing a lawsuit (said bees that were bothering him were not even on my property or on public land). However I was concerned because the hive was not in a location that technically following the regulations on placement. We ended up moving the hive to a location which placed it 8 vertical feet above the neighbor property lines to satisfy the placement requirements. I wish I could see the douche bag construction workers face when he came to the site the next day to see the hive was in a legal placement and not just removed from his baseless threats.
The second move was due to the attack on the hive by a "neighborhood flower thief". At ~2AM one September morning we heard some screaming ("Wake UP, Wake UP") and the sound of the hive falling off the retaining wall (falling wood). After working with SPD it was determined that the suspect ignored our "no trespassing" and bee warning signs and continued to pick flowers that we had near the hive. Something then caused her to knock the hive off the wall onto the ground below. After that the hive survived and still managed to rebuild and increase honey reserves.
The lack spare parts was one of the biggest things that stressed me out. After the hive attack we had 3 broken boxes, ~10 damaged frames and a damaged lid. We managed to get things put together good-enough after about 2 hours of the attack. The undamaged equipment allowed us time to get replacement parts without the hive swarming.
Due to consistent fear of the event re-occurring we decided to move the hive inside our fenced yard. While not an ideal location due to blockage of direct sun light the bees seem to be happy with it. And we have not had any additional damage to the hive.
How we did the moves
After talking with friends and Corky from Ballard Bee Company we found the best way to move the hive was a few simple steps.
- Wait until all the foragers make it back into the hive at night.
- Use an entrance reducer with a handful of grass to seal the entrance
- Move the hive (we use tie-down straps to keep the hive together)
- Place a large amount of branches and limbs to cover the entire front of the hive
- The next morning we check to make sure the bees have cleared the grass
This move process has worked well so far. Few days after each move we noticed a large number of the bees go to the old location first then make their way over to the new location. After the first move we placed a spare box, frames, lid and entrance at the old location to collect lost bees. Each night and in early morning the box was empty. We didn't put any temporary home out during the second move.
Liquid GOLD == RENT
We managed to get those bees to pay ~2.2 gallons of honey in their first year. We learned a lot around this mostly that if you are going to give honey away get the person to commit to eating it. Nothing upset me more than to find out I gave someone honey and they have never ate it. Well except for seeing the hive smashed across the ground.The big lessons over the last year were
- Have spare hive parts in the event needed
- Have some good bright flash lights in the event you need to work late
- Be ready to sprint barefoot through the neighborhood after your hive attackers
- LED lights really do mess with bees at night - We noticed once dark the bees would go to LED light bulbs but not standard incandescent bulbs
- be educated on how to calm the fears of bees and be ready to fall in love with each bee.
Friday, October 18, 2013
TFS 2012 and 2013: Changing bug WIT states breaks the ALM backlog and board with TF400917: The current configuration is not valid of this feature
Some teams have found the default bug states don't work well with their teams. Moving to the Active, Resolved, Closed workflow for the bug WIT has been the most popular with those I have worked with. I think part of this is because many teams (or their managers) are stuck in old-school waterfall mode while trying to claim they are embracing SCRUM or AGILE.
Either way if you change the BUG states from the default for the SCRUM or AGILE process template you will find the Backlog and board pages on web view for TFS 2012 or 2013 will break. If you revert your WIT change the ALM features will work again.
Broken ALM features will show: TF400917: The current configuration is not valid of this feature. This feature cannot be used until you correct the configuration.
The link to the "Learn more about configuring features" page will take you to MSDN (http://msdn.microsoft.com/en-us/library/vstudio/jj159364.aspx) which does not have any info on this.
http://blog.nwcadence.com/configure-features-in-team-foundation-server-2013/ has good info on how to setup the features if they never were setup before. But in our situation it use to work and is now BROKE!
So what's up?
A few posts will tell you to use
witadmin exportcommonprocessconfig then
witadmin importcommonprocessconfig without making any change to the .xml from the export.
This will result in an error tf400536. It will say the "requirementworkitems" entries are wrong.
So how the hell do you fix it?
You need to run
Either way if you change the BUG states from the default for the SCRUM or AGILE process template you will find the Backlog and board pages on web view for TFS 2012 or 2013 will break. If you revert your WIT change the ALM features will work again.
Broken ALM features will show: TF400917: The current configuration is not valid of this feature. This feature cannot be used until you correct the configuration.
The link to the "Learn more about configuring features" page will take you to MSDN (http://msdn.microsoft.com/en-us/library/vstudio/jj159364.aspx) which does not have any info on this.
http://blog.nwcadence.com/configure-features-in-team-foundation-server-2013/ has good info on how to setup the features if they never were setup before. But in our situation it use to work and is now BROKE!
So what's up?
A few posts will tell you to use
witadmin exportcommonprocessconfig then
witadmin importcommonprocessconfig without making any change to the .xml from the export.
This will result in an error tf400536. It will say the "requirementworkitems" entries are wrong.
So how the hell do you fix it?
You need to run
- witadmin exportcategories /p:<PROJECT> /collection:<URL TO TPC> /f:catagories.xml
- Edit the file and remove <WORKITEMTYPE name="Bug" /> from <CATEGORY refname="Microsoft.RequirementCategory" name="Requirement Category">
- witadmin importcategories /p:<PROJECT> /collection:<URL TO TPC> /f:catagories.xml
Saturday, September 7, 2013
First 3 months of Solar Panels in Seattle and we are making MONEY!
It has been ~3 months from when Puget Sound Solar installed my 4.3kW solar system.
In that time we have generated 1,910kW.
In that time we have generated 1,910kW.
Overall I am very happy with our Solar install and we have a nice credit with Seattle City light on our power bill. I pushed and rushed Puget Sound Solar to get the install completed by July 1st 2013 so we could get the WA state sales tax exception. After all that the state extended the Sales Tax exemption for some time longer. Either way it worked out and we don't appear to have any water leaks or issues on the roof.
Now the APS interface and "software" that works with the Blue Frog inverters is not the best. The web interface is hosted on a server in China and is real slow. I personally have issues having a Linux based device (the APS Communicator) sitting on my home network.
The APS Monitor (EMA Software) is the China hosted web interface that I'm not a huge fan of. I was expecting software that was going to be installed on a home computer. The web interface does not allow easy access to the data nor does it perform well when attempting to view the detailed information on each inverter.
It seems the Blue Frog inverters are branded as Made in WA but they are really manufactured by some company in China. Then they come to WA where Blue Frog does some minor change to them that makes them be classified as made in WA. Seems a little strange but hey I get .54/kWh generated because of it.
We have not yet received the paper work from the state of WA for us to provide our generation information so we can get our Generation Credit. At the current amount we are looking to get 1910 * .54 = $1031.4 from the STATE! Woot Woot.
Everyone should get solar.
Monday, August 26, 2013
AD attributes to fully Automating OS deployment with WDS (Windows Deployment Services)
Microsoft provides many tools for deploying an Operating System (Windows 7, Windows 8, Windows Server 2008, Windows Server 2012). Each has their pros and cons. Some are easy to setup some a little harder.
One thing I found over the years at Microsoft was when setting up a test system that is physical hardware it is best to do a clean install, thus using WDS.
The info below was used as part of the Automation system I developed while at the Microsoft Enterprise Engineering Center. By using WDS with the automation system we were able to simply click on a server in the UI and select Image. This would then kick off the OS Imaging workflow.
Deployment Steps:
I am not going to go into detail on how to setup WDS or add boot or install images. This is well documented on MSDN. http://technet.microsoft.com/en-us/library/jj648426.aspx.
I don't recommend using Stand-alone mode if you need a system that needs some resiliency. The AD integrated option works the best!
A few requirements
3 common values
This attribute holds the following items for a ZERO Touch deployment.
Example: to use boot image foo, unattend bar and NOT join the domain
netbootMirrorDataFile=JoinDomain=0;BootImagePath=Boot\x86\Images\foo.wim;WdsUnattendFilePath=wdsclientunattend\bar.xml;
Note the trailing ;. See http://www.mikepoulson.com/2013/08/bindlsvc-error-522-from-wds.html for more info.
These items can also be set by running the following WDSUtil commands
One thing I found over the years at Microsoft was when setting up a test system that is physical hardware it is best to do a clean install, thus using WDS.
The info below was used as part of the Automation system I developed while at the Microsoft Enterprise Engineering Center. By using WDS with the automation system we were able to simply click on a server in the UI and select Image. This would then kick off the OS Imaging workflow.
Deployment Steps:
- Power Server off (via Raritan Switched PDUs and SNMP)
- Move Network interfaces for server into correct Vlan (via Network Vlan plugin system)
- Set Active Directory Attributes below for WDS
- Set KVM interface Name (via Raritan Command Center APIs)
- Power Server ON (via Raritan Switches PDUs)
- No image to maintain for each model
- No images to patch each month
- System is clean and pure
- Bases (install) images are easy to setup (just need the .WIM from the CD)
- Does not require agents to be installed on everything
- Allows for an easy system audit script to be run at the same time
- Don't have to worry about a "safe OS" being installed on some drive
I am not going to go into detail on how to setup WDS or add boot or install images. This is well documented on MSDN. http://technet.microsoft.com/en-us/library/jj648426.aspx.
I don't recommend using Stand-alone mode if you need a system that needs some resiliency. The AD integrated option works the best!
A few requirements
- WDS is setup with a boot and install image (and you know the names(
- You have a computer object created with the UUID (netbootGuid) property set
- netbootMachineFilePath - http://technet.microsoft.com/en-us/library/cc753134(v=WS.10).aspx
- netbootMirrorDataFile - http://msdn.microsoft.com/en-us/library/dd871382.aspx
netbootMachineFilePath
This attribute specifies what PXE client should be used when the computer boots. You can use wdsutil /Set-Device /Device:<name> /BootProgram:<path> to set this.3 common values
- <WDS Server FQDN>\boot\x86\pxeboot.n12 - Tells the PXE client to NOT required F12. If you used the N12 option be sure to run "wdsutil /set-server /resetbootprogram:yes" on the WDS server. If you don't the clients will always be in reboot loop.
- <WDS Server FQDN>\boot\x86\abortpxe.com - Tells the PXE client to abort any PXE boot attempt on that NIC and move to next device in boot order
- <WDS Server FQDN>\boot\x86\pxeboot.com - Tells PXE to request the user to push F12. Will move to next boot device if they don't.
netbootMirrorDataFile
This attribute holds the following items for a ZERO Touch deployment.
- What boot file (WIM) to use - BootImagePath
- Path to unattend XML file (provides path to install WIM) - WdsUnattendFilePath
- If you want the computer joined to the domain - JoinDomain
Example: to use boot image foo, unattend bar and NOT join the domain
netbootMirrorDataFile=JoinDomain=0;BootImagePath=Boot\x86\Images\foo.wim;WdsUnattendFilePath=wdsclientunattend\bar.xml;
Note the trailing ;. See http://www.mikepoulson.com/2013/08/bindlsvc-error-522-from-wds.html for more info.
These items can also be set by running the following WDSUtil commands
- WDSUTIL /Set-Device /Device:<name> /WDSClientUnattend:<path>
- WDSUTIL /Set-Device /Device:<name> /BootImagePath:<path>
- WDSUTIL /Set-Device /Device:<name> /JoinDomain:No
Friday, August 23, 2013
Getting detailed TFS build log ActivityLog.xml information via APIs
The TFS Build Activity logs have lots of useful information. Unfortunately this information is not real easy to get to. Using the TFS 2012 APIs we can pull the log info out of the TFS SQL DB without having access to the DB. This makes it nice because we also don't have to try to parse the activitylog.xml files.
Once you have the information you can start to track and trend how long each TFS build definition takes at each part of the build. Once you have that piece of data you can start to determine where the best place to optimize to further reduce build times.
The sample code below looks up the LKG build based off the build Definition. The LKG (Last Known Good) build will be set based on the last build for that build definition that passed/succeeded. It will do a simple output of the activity name and the time it took to complete. You can take this data along with the buildDetail and create a gantt chart that shows time for each step.
The build may be old (aka not a current LKG) depending on how your system works. But I found it is the best place to start. It might be useful for some teams to import all build (success and fail) so you can determine if there is a trend area of where things are failing.
Usings and references
Once you have the information you can start to track and trend how long each TFS build definition takes at each part of the build. Once you have that piece of data you can start to determine where the best place to optimize to further reduce build times.
The sample code below looks up the LKG build based off the build Definition. The LKG (Last Known Good) build will be set based on the last build for that build definition that passed/succeeded. It will do a simple output of the activity name and the time it took to complete. You can take this data along with the buildDetail and create a gantt chart that shows time for each step.
The build may be old (aka not a current LKG) depending on how your system works. But I found it is the best place to start. It might be useful for some teams to import all build (success and fail) so you can determine if there is a trend area of where things are failing.
Usings and references
using Microsoft.TeamFoundation.Build.Client; using Microsoft.TeamFoundation.Client;logic
Uri tfsCollectionURL = new Uri("http://TFSSERVER:8080/tfs/TFSTPC");
string tfsProjectName = "TFSPROJECTNAME";
string buildDefName = "BUILDDEFINITIONNAME";
var tfs = TfsTeamProjectCollectionFactory.GetTeamProjectCollection(tfsCollectionURL);
IBuildServer buildServer = (IBuildServer)tfs.GetService(typeof(IBuildServer));
var _buildDefinitions = buildServer.QueryBuildDefinitions(tfsProjectName);
foreach (var buildDefinition in _buildDefinitions)
{
if (buildDefinition.LastGoodBuildUri == null)
continue;
var build = buildServer.GetBuild(buildDefinition.LastGoodBuildUri);
IBuildDetail buildDetail = buildServer.GetAllBuildDetails(build.Uri);
var activityTrackingNodes = InformationNodeConverters.GetActivityTrackingNodes(buildDetail);
foreach (var activity in activityTrackingNodes)
{
if (activity.State != "Canceled" && (activity.Node.Children.Nodes.Count() == 0 || (activity.Node.Children.Nodes.Any(x => x.Type == "BuildMessage") && activity.DisplayName != "Sequence")))
{
if (activity.FinishTime.ToString() == "1/1/0001 12:00:00 AM")
continue;
Console.WriteLine(activity.DisplayName + ":" + (activity.FinishTime - activity.StartTime));
}
}
}
Thursday, August 15, 2013
BINDLSVC error 522 from WDS
If you are getting BINDLSVC error 522 errors in the event log when attempting to boot systems into WDS you may have a bad config in AD.
Normally this is because when setting values to the netbootMirrorDataFile attribute on the computer object the ; (semicolon) at the end of line may be missing.
From MSDN:
netbootMirrorDataFile: This attribute is used to store multiple values in the following format:
<key-1>=<value-1>; <key-2>=<value-2>;...; <key-N>=<value-N>;
Normally this is because when setting values to the netbootMirrorDataFile attribute on the computer object the ; (semicolon) at the end of line may be missing.
From MSDN:
netbootMirrorDataFile: This attribute is used to store multiple values in the following format:
<key-1>=<value-1>; <key-2>=<value-2>;...; <key-N>=<value-N>;
Wednesday, August 14, 2013
Using C# to manage a Brocade TurboIron switch via SNMP
This is to provide an example of a plug-in I wrote for the Green Monster System to manage a Brocade TurboIron switch.
The SNMP commands are performed via a base class that uses Nsoftware's SSNMP library. That class is not included because of licensing.
If you need help or have questions feel free to send me mail.
The SNMP commands are performed via a base class that uses Nsoftware's SSNMP library. That class is not included because of licensing.
If you need help or have questions feel free to send me mail.
public bool createVlan(int tag, string name)
{
SNMPDataCollection request = new SNMPDataCollection();
request.Add(new SNMPData("1.3.6.1.2.1.17.7.1.4.3.1.1." + tag, name, datatypes.str)); //Set Vlan Name
request.Add(new SNMPData("1.3.6.1.2.1.17.7.1.4.3.1.2." + tag, new Byte[1], datatypes.str)); //Egress Members
request.Add(new SNMPData("1.3.6.1.2.1.17.7.1.4.3.1.4." + tag, new Byte[1], datatypes.str)); //Untagged Members
request.Add(new SNMPData("1.3.6.1.2.1.17.7.1.4.3.1.5." + tag, 4, datatypes.integer)); //Create and Go
return sendSNMP(request);
}
public bool deleteVlan(int tag)
{
try
{
return sendSNMP("1.3.6.1.2.1.17.7.1.4.3.1.5." + tag, 6);
}
catch (Exception)
{
//return false;
throw;
}
}
public bool addPortToVlan(int tag, int ifIndex, bool isTagged)
{
try
{
Byte[] currentMembers;
if (GetQBridgeVlanMembers(tag, isTagged, out currentMembers) == false)
return false;
if (currentMembers.Length == 0)
return false; //vlan needs to be created first
currentMembers = this.GeneratePortByteStream(ifIndex, currentMembers, true);
//Due to bug in TI code
//Issue is that QBridge Add port to vlan logic does not check if target port is tagged
//So if target port is already member of another vlan as tagged it will fail on all other adds
//Workaround is to use 1.3.6.1.4.1.1991.1.1.3.2.6.1.3.. i 4 to add the port to the vlan after it is added via qbridge to first
//If we are doing tagged we need to do workaround
if (isTagged)
{
//check to see if target port is already member of another vlan
if (isPortTaggedinAnotherVlan(ifIndex) == true)//add with private OID
return sendSNMP("1.3.6.1.4.1.1991.1.1.3.2.6.1.3." + tag + "." + ifIndex, 4);
//if this did not find and return fall through to qbridge
}
return SetQBridgeVlanMembers(currentMembers, tag, isTagged);
}
catch (Exception e)
{
// return false;
throw e;
}
}
public bool removePortFromVlan(int tag, int ifIndex)
{
try
{
return sendSNMP("1.3.6.1.4.1.1991.1.1.3.2.6.1.3." + tag + "." + ifIndex, 3);
}
catch (Exception e)
{
//return false;
throw e;
}
}
public bool? isPortInVlan(int tag, int ifIndex, bool tagged)
{
Raw_VlanMemberCollection members = GetVlanMembers(tag);
if (members.isErrorState == true)
return null;
if (members.Find(v => v.PortifIndex == ifIndex && v.isTagged == tagged) != null)
return true;
else
return false;
}
public VlanCollection getVlans()
{
VlanCollection Vlans = new VlanCollection();
SNMPDataCollection data = walk("1.3.6.1.4.1.1991.1.1.3.2.7.1.1");
if (data.isErrorState == true)
{
Vlans.isErrorState = true;
return Vlans;
}
foreach (SNMPData obj in data)
{
string vlanTag = obj.value.ToString(); //each value will be a vlan
string oid = obj.oid;
int ifIndex = Convert.ToInt32(vlanTag); //Foundry uses the Tag as the ifIndex
//Will include 4095 which is the Management Vlan
Vlan vlan = new Vlan();
vlan.tag = Convert.ToInt32(vlanTag);
vlan.ifIndex = Convert.ToInt32(ifIndex);
vlan.name = getVlanName(vlan.tag);
Vlans.Add(vlan);
}
return Vlans;
}
public Raw_VlanMemberCollection GetVlanMembers(int tag)
{
Raw_VlanMemberCollection data = new Raw_VlanMemberCollection();
//Get untagged port members
byte[] vlanMembers;
if (GetQBridgeVlanMembers(tag, false, out vlanMembers) == false)
{
data.isErrorState = true;
return data;
}
ArrayList members = GetMemberPorts(vlanMembers);
foreach (int p in members)
{
Raw_VlanMember member = new Raw_VlanMember();
member.isTagged = false;
member.tag = tag;
member.port = p;
member.slot = 1; //only supports 1 slot
member.PortifIndex = p;
member.VlanifIndex = tag;
data.Add(member);
}
//Get Tagged members
if (GetQBridgeVlanMembers(tag, true, out vlanMembers) == false)
{
data.isErrorState = true;
return data;
}
members = GetMemberPorts(vlanMembers);
foreach (int p in members)
{
Raw_VlanMember member = new Raw_VlanMember();
member.isTagged = true;
member.tag = tag;
member.port = p;
member.slot = 1; //only supports 1 slot
member.PortifIndex = p;
member.VlanifIndex = tag;
data.Add(member);
}
return data;
}
public NetworkPortCollection getPorts()
{
NetworkPortCollection Ports = new NetworkPortCollection();
SNMPDataCollection data = walk("1.3.6.1.4.1.1991.1.1.3.3.5.1.18");
if (data.isErrorState == true)
{
Ports.isErrorState = true;
return Ports;
}
foreach (SNMPData obj in data) //Get all our ports
{
string value = obj.value.ToString();
//static devices dont return slot info
if (value.IndexOf(@"/") == -1) //we are not doing a moduler device
{
value = @"1/" + value; //set default slot to 1
}
//Value returned looks like /
//Lets split it out
string[] slotport = value.Split(Convert.ToChar(@"/"));
int id = Convert.ToInt32(obj.oid.ToString().Substring(obj.oid.ToString().LastIndexOf(".") + 1));
NetworkPort port = new NetworkPort(id, Convert.ToInt32(slotport[0]), Convert.ToInt32(slotport[1]));
port.name = getPortDescription(port.ifIndex);
port.adminStatus = getPortAdminStatus(port.ifIndex);
port.operationStatus = getPortOperationStatus(port.ifIndex);
if (port.unTaggedVlan == null)
{
port.unTaggedVlan = new VlanMember();
}
port.unTaggedVlan.tag = getPortUntaggedVlan(port.ifIndex);
Ports.Add(port);
}
return Ports;
}
public bool addFirstPorttoVlan(int tag, int ifIndex, bool isTagged, string vlanName)
{
try
{
SNMPDataCollection request = new SNMPDataCollection();
request.Add(new SNMPData("1.3.6.1.2.1.17.7.1.4.3.1.1." + tag, vlanName, SNMPBase.datatypes.str)); //Vlan Name
request.Add(new SNMPData("1.3.6.1.2.1.17.7.1.4.3.1.2." + tag, new byte[1], SNMPBase.datatypes.str)); //Egress Ports
if (isTagged)
request.Add(new SNMPData("1.3.6.1.2.1.17.7.1.4.3.1.4." + tag, new byte[1], SNMPBase.datatypes.str)); //Untagged ports
request.Add(new SNMPData("1.3.6.1.2.1.17.7.1.4.3.1.5." + tag, 4, SNMPBase.datatypes.integer)); //CreateAndGo
bool response = sendSNMP(request);
return addPortToVlan(tag, ifIndex, isTagged);
}
catch (Exception)
{
return false;
throw;
}
}
private int getPortUntaggedVlan(int ifIndex)
{
string data;
if (getSNMP("1.3.6.1.4.1.1991.1.1.3.3.5.1.24." + ifIndex.ToString(), SNMPBase.datatypes.integer, out data) == false)
return -1;
return Convert.ToInt32(data);
}
#region "Private"
private bool isPortTaggedinAnotherVlan(int ifIndex)
{
VlanCollection vlans = getVlans();
foreach (Vlan vlan in vlans)
{
Raw_VlanMemberCollection members = GetVlanMembers(vlan.tag);
Raw_VlanMember member = members.Find(m => m.PortifIndex == ifIndex && m.isTagged == true);
if (member != null)
return true; //found a member
}
return false; //port is not any other vlans
}
private static readonly Byte[] PORTMASKARRAY = { 128, 64, 32, 16, 8, 4, 2, 1 };
private ArrayList GetMemberPorts(Byte[] memberbytes)
{
//Find out which bit positions are set in a byte. Based off which position and byte we are in we can determine the port number
//ie bit 7 in byte 0 = port 1
//ie bit 0 in byte 0 = port 8
ArrayList members = new ArrayList();
int bytecoute = 0;
int portNumber = 0;
int result = 0;
foreach (Byte b in memberbytes)
{
if (memberbytes[bytecoute] == 0)
{
bytecoute++; // No ports where active in the Byte
}
else // if we have port membership in the Byte lets see which ports
{
for (int i = 0; i < 8; i++) // Loop through each bit
{
result = memberbytes[bytecoute] & PORTMASKARRAY[i]; // Is each bit value (port) in the array?
if (result == PORTMASKARRAY[i])
{
portNumber = i + 1 + bytecoute * 8;
members.Add(portNumber); // Add the portnumber to our returned list
}
}
bytecoute++;
}
}
return members;
}
private static bool isPortMember(int portnumber, Byte[] membershipstream)
{
//Determine the port number we are working with for the given slot
//Mod by 1000 to remove slot number; set remainder to portnumber
portnumber %= 1000;
//Byte[] PORTMASKARRAY = { 128, 64, 32, 16, 8, 4, 2, 1 };
return (membershipstream[(portnumber - 1) / 8] & PORTMASKARRAY[(portnumber - 1) % 8]) != 0;
}
private bool GetQBridgeVlanMembers(int tag, bool isTagged, out Byte[] members)
{
members = new Byte[0];
if (isTagged)
{
byte[] allmembers;
getSNMP("1.3.6.1.2.1.17.7.1.4.2.1.4.0." + tag, out allmembers); //dot1qVlanStaticEgressPorts
//dot1qVlanStaticEgressPorts also includes untagged ports. So we have to remove the untagged ports from the array.
//What is left will be the tagged only ports
byte[] untaggedMembers;
if (GetQBridgeVlanMembers(tag, false, out untaggedMembers) == false)
return false;
byte[] taggedports = new byte[allmembers.Length];
for (int i = 0; i < allmembers.Length; i++)
{
taggedports[i] = Convert.ToByte(allmembers[i] ^ untaggedMembers[i]);
}
members = taggedports;
return true;
}
else
{
byte[] untaggedMembers;
if (getSNMP("1.3.6.1.2.1.17.7.1.4.2.1.5.0." + tag, out untaggedMembers) == false) //dot1qVlanStaticUntaggedPorts
return false;
members = untaggedMembers;
return true;
}
}
private bool SetQBridgeVlanMembers(Byte[] members, int tag, bool isTagged)
{
//Allways add Vlan member to Egress port. By default is tagged. If you want untagged then also add to untaggedPorts
bool result = false;
if (isTagged)
{
return sendSNMP("1.3.6.1.2.1.17.7.1.4.3.1.2." + tag, members, datatypes.str); //dot1qVlanStaticEgressPorts
}
else
{
//for egreess ports we need to get all current egress ports to append the current port
Byte[] currentMembers;
if (GetQBridgeVlanMembers(tag, true, out currentMembers) == false)
return false;
if (currentMembers.Length == 0)
return false; //vlan needs to be created first
result = SetQBridgeVlanMembers(currentMembers, tag, true);
if (result == false)
return false;
result = sendSNMP("1.3.6.1.2.1.17.7.1.4.3.1.4." + tag, members, datatypes.str); //dot1qVlanStaticUntaggedPorts
}
return result;
}
///
/// QBridge Byte Stream generation, used for modifing port membership in Vlan
///
/// portnumber to add/remove from vlan
///
private Byte[] GeneratePortByteStream(int newMemberPort, Byte[] currentMembers, bool addPort)
{
//Determine the port number we are working with for the given slot
//Mod by 1000 to remove slot number; set remainder to portnumber
//portnumber %= 1000;
Byte[] holdingByte = new Byte[currentMembers.Length];
Array.Copy(currentMembers, holdingByte, currentMembers.Length);
//Determin which byte we are working with
int byteposition = (newMemberPort - 1) / 8;
//Mod to find the bit we are working with
int maskindex = (newMemberPort - 1) % 8;
//Set the value in our Holding array for the corresponding bit
if (addPort)
holdingByte[byteposition] |= PORTMASKARRAY[maskindex];
else
{
if ((holdingByte[byteposition] & PORTMASKARRAY[maskindex]) == 0) //check to see if the newMemberport is a member of the currentMembers
return holdingByte;
if (currentMembers[byteposition] == 0) //dont xor a null value. If the port is not already a member dont try to remove it.
return holdingByte;
// holdingByte[byteposition] = Convert.ToByte(currentMembers[byteposition] & INVERTED_PORTMASKARRAY[maskindex]);
holdingByte[byteposition] = Convert.ToByte(currentMembers[byteposition] ^ PORTMASKARRAY[maskindex]);
}
return holdingByte;
}
private string ConvertToHex(object bytearray)
{
byte[] ba = (byte[])bytearray;
StringBuilder hex = new StringBuilder(ba.Length * 2);
foreach (byte b in ba)
hex.AppendFormat("{0:x2}", b);
return hex.ToString();
}
#endregion
Post 4: Green Monster Network plugin examples
Part of the Green Monster Project is to
abstract the hardware for Vlan Management.
To do this I created a “plugin” for each network switch that we use at
the EEC. When action needs to be taken
against a network switch. Green Monster
first queries the database (See post 2)
to determine the DLL that it should load into the plugin manager. Once it is loaded I can execute the exposed
methods.
This plugin model is the same process used for Power
management and KVM management. The
interaction with the network switch is abstracted from the main code. It is up to the plugin to know the best ways
to execute a given command. This allows
for the plugin to use any available execution methods. This can include SNMP, Netconf, scripted
telnet, SSH, or other web services. As
long as the plugin has the ability to communicate via the protocol desired it
should work.
The majority of the code we use for device interaction is
SNMP. So I thought I would post how we
use SNMP to do Vlan management. Some of
this was found by doing packet sniffs and working with the Manufactures
engineers.
For SNMP in CSharp I use the nsoftware.com SSNMP
library. It is very fast and we had luck with it working well. The code to
make the “raw” snmp call is not included because that requires a license for
the nsoftware library. If you have that
license and are interested please let me know and I can share some snip-its.
Brocade Networks (Turborion, MLX)
Post 3: Green Monster Plugin system
Part 3 of N. See Part 2 or Part 1
The plugin system allowed for 3 “device types” that I could automate. KVM Switch, Network Switch and Power Distribution unit (PDU). I created an interface for each that the plugin had to implement to work with Green Monster.
Green Monster Plugin design
One of Green Monster’s primary goals was to provide a
framework to automate tasks against Network Switches, KVM Switches, Rack PDUs,
Blade Chassis Management Interfaces, iLO/DRAC.
By abstracting the network design and underling hardware my team members
do not need to know how all the network switches interconnect or how to use the
CLI/GUI for each manufacture/model.
I created a plugin framework where each model references a
DLL file that is to be loaded when action against it is to be taken. This Plugin Name field (see Green Monster
Database design post) exists on the Model level and the asset attribute level. This allows for a specific (maybe version
specific) dll to be used on a single asset or a version agnostic DLL to be used
on all common models.
When a device action
is being performed Green Monster will run the get plugin sproc that first
checks to see if the “asset” as a custom plugin. If not it looks up the plugin for the
model. This seemed to work well when a
single device had a different version of software (say Switch firmware) that
broke with the Model default plugin. It
also allowed a way to prototype a plugin without rolling it out to all devices.
The plugin system allowed for 3 “device types” that I could automate. KVM Switch, Network Switch and Power Distribution unit (PDU). I created an interface for each that the plugin had to implement to work with Green Monster.
I will mostly focus
on the network devices because they were one of the main goals with Green
Monster.
SNMP was the most used protocol when working with devices. Though there is nothing in the system that required it to be. I wrote a power plugin for the HB C-7000 blade chassis OA that utilized scripted telnet. While it was slow it worked well where the manufactures had lacking APIs/interfaces for automating their devices.
Post 2: Green Monster DB Info
The key to Green Monster is the database. The database holds information about “assets”
which are any physical item that should be inventoried or will be used for
automation. For the EEC we inventory
Servers, Laptops, desktops, Rack PDUs, KVM Switches, Network Switches, HP OA
interfaces, Blade Chassis, SAN storage, PCIe Flash Storage (Fusion IO), Load
Balancers (F5, Brocade), Encryption devices (nCypher HSM), Racks, Blades, DAS
shelves (MSA 70), and many other things.
The database has tables for Manufactures, Models, Device
Type (Network Switch, KVM switch, other items from above). An “asset” links to a Model which then links
to Device Type and Manufacture.
99% of Assets will have common attributes (though with
different values), for example Serial Number, warranty start Date, Cost. Some additional values that we find most of
our Assets have include parent Asset (for child parent relationship), status code, Purchase Order number, Asset tag
(inventory tracking).
CPU ID from above links to a table which tracks information about what CPUs exit in a Server. This allows us to track usage of CPU features tested and also find systems that meet specific customer requirements for a scenario.
The Models table which has a direct relationship to the
Assets table as the following fields, Model Name, Model Number, Code Name, Size
in RU, Weight, Power Voltage, Power Current, and like the attributes table
there is a plugin name field (more on this field in the following post).
When the EEC does performance or scale testing on equipment
it is important to know the exact specifications of all the systems that were
used in the test. These systems can be
all the same model or a mix of model, manufacture, architecture (Intel,
AMD). Over the life cycle of a server we
will upgrade the CPUs to faster (maybe pre-release) options, or change the
memory speed/quantity. Even with these changes
we need to be track what the exact specs were at any given point in time. The solution to that issue is an Audit
table. The Audit table is populated from
triggers that exist on the Asset, attribute and CPU tables. It tracks current and previous values anytime
a field is changed in one of the monitored tables.
With these tables and a few others we have a DB design for
basic inventory tracking that offers a child/parent relationship, tracks
information about specific systems, provides a history of a Servers
upgrade/downgrade and will scale very well.
For the automation pieces of Green Monster to work it relies on knowing how each component (Network, Server, NIC, Power Port, KVM) all relate to each other.For Network and Power control each asset has a 1 to many relationship. This means that a Server can have >1 network port and >1 Power ports.
Each port has a “parent”
of the asset and a neighbor of where it connects to. If a Server has 2 nics it will have 2 entries
in the network ports table. If a switch
has 48 ports it will have 48 entries in the table. From that a simple mapping from server nic 1
to switch port 1 can be done. This
allows the automation pieces to know how everything relates.
For network ports
the MAC address of the NIC is also tracked.
This is used for assigning IP address during the OS image phase and for
the “audit” tool to make sure that a Mac address is showing on the correct
Network switch port when dumping the MAC table.
More on those later.
Subscribe to:
Posts (Atom)
10 Years from last post
Well world! After the last almost 10 years I have been up to a few things in life and work. Most recently I was working at Microsoft on ...
-
The TFS Build Activity logs have lots of useful information. Unfortunately this information is not real easy to get to. Using the TFS 2012...
-
Repost from old Blog Today I had a con-call to talk about disk quota and how to automate the setting and reading of Quota. So i thought t...











