Monday, September 07, 2009

Best practice around Zenoss template bindings

After working with Zenoss Core for a few years now, I wanted to pass along a tip that would have saved me a lot of time had I known it in the beginning: do not bind performance templates to individual devices, or make local modified copies of templates on devices. Once you do this, the device no longer inherits template bindings from the parent device class, but you have no good way to know that this condition exists unless you drill into the device and examine its template bindings.

When you move a device from one class to another, if it lacks local bindings or overridden templates, it will inherit the bindings of the new class. However, this does not occur if you have made any local changes as above. Also, if you bind new performance templates to its parent, it ignores these new bindings if you have made any local changes. I highly recommend that if you need local changes to a device, make a new device class and make the template binding changes to the class. Then, move the device to the new class, and it will pick up the new bindings.

To clear this condition on a device once you have made local changes, you must reset bindings to be those of its container. On the device, go to More | Templates, then use the drop-down arrow next to "Performance templates for device," and finally Reset Bindings.

Updating the UI from a BackgroundWorker in WPF

This approach enabled me to flexibly send messages to objects on the UI thread while processing from a BackgroundWorker object in WPF. Otherwise, the UI thread doesn't update if the BackgroundWorker code tries to manipulate its objects during processing. First I created this UIUpdater:
public delegate void UpdateTextDelegate(string message);

///
/// Sends messages to the UI thread via the dispatcher.
///

class UIUpdater
{
#region ctor
///
/// Initializes a new instance of the class.
///

/// The dispatcher in scope for the UI.
/// Function that will apply the message to the appropriate control(s).
public UIUpdater(Dispatcher uiDispatcher, UpdateTextDelegate uiUpdateDelegate)
{
if (default(Dispatcher) == uiDispatcher) throw new ArgumentNullException("uiDispatcher");
if (default(UpdateTextDelegate) == uiUpdateDelegate) throw new ArgumentNullException("uiUpdateDelegate");

dispatcher = uiDispatcher;
updateTextDelegate = uiUpdateDelegate;
}
#endregion

#region member variables
Dispatcher dispatcher = default(Dispatcher);
UpdateTextDelegate updateTextDelegate = default(UpdateTextDelegate);
#endregion

#region methods
///
/// Sends the message.
///

/// The message.
public void SendMessage(string message)
{
if (default(string) == message) throw new ArgumentNullException("message"); // allow string.Empty
dispatcher.BeginInvoke(
DispatcherPriority.Normal,
(ThreadStart)delegate() {
updateTextDelegate(message);
});
}
#endregion
}

To use it, in the BackgroundWorker_DoWork event handler, initialize it and pass it into your objects that implement the background work. Give it a reference to your dispatcher and a delegate that will handle updates from the background thread:
UIUpdater updater = new UIUpdater(mainWindow.Dispatcher, new UpdateTextDelegate(UpdateStatus));

DoMyWork(updater);
Then from the code doing the background work, you can call the SendMessage delegate to safely pass along the message:
updater.SendMessage("Deleting all items from the " + listName + " list...");

Programmatically creating a recursive view with WSS web services

I had a project where I wanted to do a few things:
  • Create a view using the Windows SharePoint Services' web services
  • Make this view recursive
  • Set the display style to the alternating line style
What I discovered is that there is not feature parity between the WSS object model and the web service. Also, while it is possible to create the view and make it recursive, there is no support in the WSS 3.0 web services for setting the style on a view.

To create a recursive view:

Add Web Reference

First, add a web reference to the Views web service. You can do this by adding an ASMX web reference in your project to http://yourSharePointSiteUrl/resources/_vti_bin/views.asmx.

Create the Client
SharePointViewService.Views viewClient = new MyProject.SharePointViewService.Views();
viewClient.Credentials = System.Net.CredentialCache.DefaultCredentials;
Create the View via the Web Service
When working with the WSS web services, the basic model is to send the XML fragments to the method call and then parse the result for exceptions.
XmlNode result = viewClient.AddView(listName, viewName, viewFieldsNode, queryNode, rowLimitNode, type, false); // the last parameter: isDefaultView
Here are examples of the parameter values for the above:

listName: "My List"
viewName: "My View"
viewFieldsNode (outer XML):
<ViewFields><FieldRef Name="LinkTitle" />
<FieldRef Name="Item_x0020_Number" />
<FieldRef Name="Description" />
<FieldRef Name="Release" />
<FieldRef Name="Module" />
</ViewFields>
queryNode (outer XML):
<Query>
<OrderBy>
<FieldRef Name="Item_x0020_Number" />
</OrderBy>
<GroupBy Collapse="TRUE" GroupLimit="100">
<FieldRef Name="Release" />
<FieldRef Name="Module" />
</GroupBy>
</Query>
rowLimitNode (outer XML):
<RowLimit Paged="TRUE">100</RowLimit>
type: "HTML"

If exceptions occur, you will have meaningful data in the return value from the AddView method call.

Update the View to Set the Recursive Property
You cannot create the view as a recursive view--the key is that you must follow up the add call with an update call to set this property.
XmlDocument vp = new XmlDocument();
vp.AppendChild(vp.CreateElement("View"));
vp.DocumentElement.SetAttribute("Scope", "Recursive");
viewClient.UpdateView(addView.ListName, viewResult.Name, vp, default(XmlNode), default(XmlNode), default(XmlNode), default(XmlNode), default(XmlNode));
I sincerely hope this helps someone, as the documentation for the WSS web services, particularly the Views web service, is significantly lacking.

Tuesday, July 28, 2009

Zenoss 2.3.3 error moving device organizers

I encountered an interesting error today with Zenoss 2.3.3 (yes, 2.4 has been out for some time, but we've been having trouble getting a clean upgrade process). I had the following device classes with devices:

/Server/Windows/Sync/DatacenterA
- device1
- device2
- device3
/Server/Windows/Sync/DatacenterB
- device4
- device5
- device6

As these were all production machines, I wanted to differentiate them from the other dev and QA machines. I created the organizer /Server/Windows/Sync/Production and moved the ../DatacenterA and ../DatacenterB organizers with their devices under the new /Server/Windows/Sync/Production and...

...everything broke for the moved devices. All zencommand and zenperfsnmp-based monitoring just stopped cold. When starting zenperfsnmp in debug mode, I noticed errors on its first run like:

2009-07-27 17:15:45 WARNING zen.zenperfsnmp: Error loading config for devices ['device1', 'device2']
2009-07-27 17:15:45 WARNING zen.zenperfsnmp: Error loading config for devices ['device3', device4']
2009-07-27 17:15:45 WARNING zen.zenperfsnmp: Error loading config for devices ['device5', device6']

To correct this, I ended up moving the organizers back to their original locations, creating new organizers with the structure I wanted, and moving the individual devices (not the organizers) to the new organizer structure.

Now if I could just get this issue fixed I'd have no more zenperfsnmp/zencommand mysteries!

Friday, May 15, 2009

Zenoss zencommand daemon overload

I think we have found that the zencommand daemon in Zenoss Core has some very reachable limits with regard to the number of commands it will process. We have been increasing our command monitoring lately and got to a point where zencommand, the daemon responsible for running these types of monitoring functions, had 1,200 commands in each cycle. I noticed that some of the performance templates that graphed the counters fetched by our commands had gaps--sometimes large gaps--and some had simply quit entirely. The odd thing was that we didn't really get any warnings (our VP went looking for graphed data and, ummm, didn't find it).

If I took one of the devices with templates that weren't graphing and manually ran zencommand against that device, it worked perfectly and fetched all the counters from the various commands. Stracing it didn't show any errors either. But with the amount of load we were providing, it was definitely silently dropping commands.

Zencommand seems to be a rather single-threaded beast. Its ability to get everything done is a function of the following:
  • The number of data sources it is processing
  • The number of monitored devices
  • The cycle time of the data sources it is processing
I was able to collapse some of our data sources so that instead of 1,200 commands I got down to around 560, and voila--the graphs that had not been painting suddenly began working correctly. To avoid this issue, I recommend the following:
  • The native Device template uses SNMP (and SNMP Informant on the Windows side) to read the base CPU, memory, and paging counters. To avoid deploying SNMP Informant everywhere, some time ago we had changed to using a different template that used zencommand and remote WMI calls to read these in. I am going to change this back to using Device, which will take quite a bit of load off of zencommand.
  • Watch the cycle time. Does anyone have QoS recommendations for the resolution of performance counters? 60 second cycle times are a bit aggressive, but what works well--3 minutes? 5 minutes?
  • Always validate that the graphs are painting after making changes affecting zencommand. If you roll out a new template, don't just look at that template to make sure it's working--look at other things zencommand is handling after you roll it out. As we discovered, adding too much load can silently break other things.


Monday, May 11, 2009

Windows 7 RC x64 impressions

I rebuilt my machine due to system instability and decided to go with Windows 7 x64 this time now that we have a release candidate. So far, this is running much better than Vista ever did and some of the new features are compelling. I think my favorite feature so far is how the window manager groups related windows behind the application icon only, and--wait for it--lets you CLOSE them from the window preview when you hover over the icon. You no longer need to raise the window first and use its close controls or right-click to close-the 'x' icon is right there on the preview. This seems simple, but it's a great improvement.

Another nice feature is the selection of a wireless network. This works the same as Ubuntu does now and just shows you the available networks with a single click. Each has radio buttons to select it. No more wading through multiple screens just to change networks.

I have hit one bug: I have files on my desktop (as shown in \Users\user\Desktop), but the desktop itself is completely clean. I can right-click on the desktop and create new files, but while these appear in an Explorer folder pointed to that location, they vanish from the desktop itself. Has anyone else run into this?
** Update: I'm an idiot; it's a setting to show icons on the desktop. I am not sure why this was off by default. The setting is under Control Panel\Appearance and Personalization\Personalization\Change desktop icons, and you check User's Files.

Overall, I highly recommend giving the RC a spin.

Tuesday, May 05, 2009

Ubuntu 9.04 and .local domain access

I had to re-discover why my organization's .local internal domain wouldn't resolve on Ubuntu 9.04. I was unable to ping or RDP to any of the machines using their fully-qualified names (e.g. server.domain.local), although this DID work if I used the NetBIOS names (e.g. server). After researching for a bit, I rediscovered something someone helped me with a long time ago when I thought this issue was related to DNS resolution over a PPTP VPN connection (it is not in fact related to this).

The issue: the avahi daemon. As I understand it, it grabs the .local domain as its own and interferes with ping, RDP, and other communication when using the fully-qualified .local domain names. There are two fixes I know of:

  1. Disable avahi. I understand this can interfere with apps that use avahi, so...
  2. Edit the hosts line if /etc/nsswitch.conf to read:
FROM:
hosts: files mdns4_minimal [NOTFOUND=return] dns mdns4

TO:
hosts: files dns mdns4_minimal mdns4

Wednesday, February 25, 2009

IIS7 and net.tcp only WAS hosting

I was interested in having net.tcp-only hosting (no HTTP) for a WAS-activated WCF service in IIS7. I found that IIS uses icons that trick you into thinking the site is in an error state when in reality it is not. Here are the steps I took to host my service:


Create the Website

I didn't want to use a virtual directory to host my service, so I created a new web site in IIS Manager (right-click Sites, then Add Web Site). The key here is to select the net.tcp binding type and specify the port in the Binding Information section:



After doing this, IIS7 will show the website in what appears to be an error state (it will have a red 'X' next to the website icon and many of the right-panel Actions menu options (notably the entire Manage Web Site section, containing the restart/start/stop controls and Advanced Settings link) will not appear. THIS SEEMS TO BE A RED HERRING. It will show the red 'X' whenever there are no HTTP bindings for the service. Here is what it looked like on a different site I set up in this manner:



The service will function in spite of the fact that it doesn't show as started. Starting/stopping the service must be done via the controls on the site's application pool (right-clicking it works):




Add Filesystem Components

At a minimum, you need your .svc file, a web.config file, and a bin folder containing the assembly(ies) that implement your service in the website root folder:



The .svc file simply contains the declaration of the class that implements the service:
<%@ServiceHost language="c#" Debug="true" Service="Microsoft.ServiceModel.Samples.CalculatorService" %>
In the web.config, you can omit all references to HTTP bindings, and set up the Metadata Exchange (mex) endpoint to use the net.tcp binding. You can even omit that if you don't need the service to be discoverable, although it helps to retain it:
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <system.serviceModel>
    <services>
      <service
          name="Microsoft.ServiceModel.Samples.CalculatorService"
          behaviorConfiguration="CalculatorServiceBehavior">

        <endpoint    address=""
                    binding="netTcpBinding"
                    contract="Microsoft.ServiceModel.Samples.ICalculator" />
        <endpoint    address="mex"
                    binding="mexTcpBinding"
                    contract="IMetadataExchange" />
      </service>
    </services>

    <!--For debugging purposes set the includeExceptionDetailInFaults attribute to true-->
    <behaviors>
      <serviceBehaviors>
        <behavior name="CalculatorServiceBehavior">
          <serviceMetadata /> <!--You do not need this node if you remove the mex endpoint-->
          <serviceDebug includeExceptionDetailInFaults="False" />
        </behavior>
      </serviceBehaviors>
    </behaviors>
  </system.serviceModel>
</configuration>
Note that I did not specify any port information in the address for the endpoint using netTcpBinding as I wanted it to simply use the binding specified for the website (in the case of the website I showed creating up top, TCP over port 11111).


Create a Client

After this, you should be able to point to the service via net.tcp and generate a proxy. In Visual Studio 2008, you can use the Add Service Reference facility in your client project by specifying the following, substituting your service's TCP port for the '11111':
net.tcp://localhost:11111/service.svc/mex





Monday, February 23, 2009

Error on MOSS list submissions after December 2008 cumulative update

I recently patched my MOSS development environment to the post-SP1 December 2008 cumulative updates. The patch applied successfully (although it did require two reboots, one for the WSS cumulative update and a second for the MOSS one). In testing the site after the update, everything seemed to work well until I tried to add an item to a list. I then got this "Page has been modified since you opened it" error:



I followed the "refresh page" link and re-attempted my submission, and it succeeded the second time. I then tried a different list and received the same results. It seems that each list needs to be touched once before new list submissions will succeed.

** UPDATE: Perhaps there is a timer job that is fixing the lists. I tried another test after waiting a bit, and this time, I did not get the above refresh error upon making the first list submission. Patience may be the applicable virtue here.

Thursday, February 19, 2009

Visual Studio 2008 x64: The Project Location is Not Trusted

Aargh... here's hoping I can save someone else some time on this. I raised a new Windows Server 2008 x64 .NET build machine and loaded up Visual Studio 2008 on there. I then tried to open up a project stored on a network share via a UNC path (\\server\share\my.sln) and got the dreaded "project location is not trusted" dialog.

I started looking for the .NET Framework 2.0 Configuration utility in the Administrative Tools area so I could grant FullTrust to my share. It isn't there. Apparently it used to deploy as part of the .NET Framework 2.0 SDK, but the .NET 3.5 (Windows 2008) SDK no longer includes it. This is a shame because the alternative, CasPol.exe, is harder to work with.

At any rate, I fired up a Windows Server 2008 command shell and ran the following command to trust my network share (for those not familiar with CasPol and its syntax, here is an excellent post):
caspol.exe -m -ag 1.2 -url file://\\server/share/* FullTrust
However, upon opening my project, I was continuing to get prompted that the location was not trusted. Finally I noticed that the Windows Server 2008 command shell was an x64 command shell... and Visual Studio 2008 is a 32-bit (x86) application. You have to set the security policy separately for the appropriate version of the .NET Framework.

I then navigated to the x86 version of caspol (%windir%\Microsoft.NET\Framework\v2.0.50727\caspol.exe), issued the same command... and voila, no more prompting!



Tuesday, January 27, 2009

Guidance on Ahead of Time Compilation for Mono 2.2?

Does anyone have recommendations on using ahead of time compilation for the system libraries under Mono 2.2? The Mono website has this article with some (outdated?) information about how to carry it out. I tried following the instructions with the following exceptions:
  • I found multiple mscorlib.dll assemblies (at /usr/lib/mono/1.0, 2.0, 2.1) and tried AOT-compiling all of them. Only the 1.0 and 2.0 assemblies succeeded. The 2.1 assembly kept referring to the 2.0 assembly and never produced the .so file for itself.
  • Several assemblies (System.Xml.dll for one) in the GAC failed to precompile. The AOT compilation process for that one complained that it could not load the System assembly.
When I tried executing Mono executables (themselves also AOT-compiled and working prior to my pre-compilation of the system assemblies), they universally failed with Mono framework errors.

Has anyone successfully AOT-compiled the system assemblies under Mono 2.2? Is this even a good idea, assuming there's a way to make it work?

Monday, January 19, 2009

Yet another WPF HyperlinkButton control

I wanted a general purpose implementation of the Silverlight HyperlinkButton for WPF that supported binding things like the font, font weight, etc. so I quickly rolled this puppy. It has its own Click event so clients can subscribe appropriately. I am sure there are some enhancements lurking, but it seems to do the job:

XAML:

<UserControl x:Class="Controls.HyperlinkButton"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    x:Name="root"
    >
    <TextBlock x:Name="hyperlinkContentText"
        Text="{Binding HyperlinkContent, ElementName=root}"
        FontSize="{Binding FontSize, ElementName=root, Path=FontSize}"
        FontFamily="{Binding FontFamily, ElementName=root, Path=FontFamily}"
        FontStretch="{Binding FontStretch, ElementName=root, Path=FontStretch}"
        FontStyle="{Binding FontStyle, ElementName=root, Path=FontStyle}"
        FontWeight="{Binding FontWeight, ElementName=root, Path=FontWeight}"
        Foreground="{Binding Foreground, ElementName=root, Path=Foreground}"
        Background="{Binding Background, ElementName=root, Path=Background}"
        Opacity="{Binding Opacity, ElementName=root, Path=Opacity}"
        OpacityMask="{Binding OpacityMask, ElementName=root, Path=OpacityMask}"
        MouseEnter="hyperlinkContentText_MouseEnter"
        MouseLeave="hyperlinkContentText_MouseLeave"
        MouseLeftButtonDown="hyperlinkContentText_MouseLeftButtonDown" />
</UserControl>

Code-behind:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;

namespace Controls
{
    /// <summary>
    /// Interaction logic for HyperlinkButton.xaml
    /// </summary>
    public partial class HyperlinkButton : UserControl
    {
        #region dependency properties
        /// <summary>
        /// Dependency property for hyperlink text content.
        /// </summary>
        public static readonly DependencyProperty HyperlinkContentProperty;
        #endregion

        #region routed events
        /// <summary>
        /// Routed event for handling the "button" click.
        /// </summary>
        public static readonly RoutedEvent ClickEvent;
        #endregion

        #region ctor
        #region static
        /// <summary>
        /// Initializes the <see cref="HyperlinkButton"/> class.
        /// </summary>
        static HyperlinkButton()
        {
            FrameworkPropertyMetadata fpm = new FrameworkPropertyMetadata(string.Empty);
            HyperlinkContentProperty = DependencyProperty.Register(
                "HyperlinkContent",
                typeof(string),
                typeof(FrameworkElement),
                fpm);

            ClickEvent = EventManager.RegisterRoutedEvent(
                "Click", RoutingStrategy.Bubble, typeof(RoutedEventHandler), typeof(HyperlinkButton));
        }
        #endregion

        #region instance
        /// <summary>
        /// Initializes a new instance of the <see cref="HyperlinkButton"/> class.
        /// </summary>
        public HyperlinkButton()
        {
            InitializeComponent();
        }
        #endregion
        #endregion

        #region properties
        /// <summary>
        /// Gets or sets the content of the hyperlink.
        /// </summary>
        /// <value>The content of the hyperlink.</value>
        public string HyperlinkContent
        {
            get { return (string)GetValue(HyperlinkContentProperty); }
            set { SetValue(HyperlinkContentProperty, value); }
        }

        /// <summary>
        /// Occurs when [click].
        /// </summary>
        public event RoutedEventHandler Click
        {
            add
            {
                AddHandler(ClickEvent, value);
            }
            remove
            {
                RemoveHandler(ClickEvent, value);
            }
        }
        #endregion

        #region event handlers
        /// <summary>
        /// Handles the MouseEnter event of the hyperlinkContentText control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.Windows.Input.MouseEventArgs"/> instance containing the event data.</param>
        private void hyperlinkContentText_MouseEnter(object sender, MouseEventArgs e)
        {
            hyperlinkContentText.TextDecorations = TextDecorations.Underline;
            hyperlinkContentText.Cursor = Cursors.Hand;
        }

        /// <summary>
        /// Handles the MouseLeave event of the hyperlinkContentText control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.Windows.Input.MouseEventArgs"/> instance containing the event data.</param>
        private void hyperlinkContentText_MouseLeave(object sender, MouseEventArgs e)
        {
            hyperlinkContentText.TextDecorations = null;
            hyperlinkContentText.Cursor = Cursors.Arrow;
        }

        /// <summary>
        /// Handles the MouseLeftButtonDown event of the hyperlinkContentText control. Maps it to the click
        /// event of this control and suppresses it after raising Click.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.Windows.Input.MouseButtonEventArgs"/> instance containing the event data.</param>
        private void hyperlinkContentText_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
        {
            e.Handled = true;
            RaiseEvent(new RoutedEventArgs(ClickEvent, this));
        }
        #endregion
    }
}