monitoring/api/AlertSample/Program.cs (337 lines of code) (raw):
/**
* Copyright 2017, Google, Inc.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* This application demonstrates how to perform basic operations on alerting
* policies with the Google Stackdriver Monitoring API.
*
* For more information, see https://cloud.google.com/monitoring/docs/.
*/
using CommandLine;
using Google.Api.Gax.ResourceNames;
using Google.Cloud.Monitoring.V3;
using Google.Protobuf;
using Google.Protobuf.WellKnownTypes;
using Grpc.Core;
using Newtonsoft.Json;
using Newtonsoft.Json.Converters;
using System;
using System.Collections.Generic;
using System.IO;
namespace GoogleCloudSamples
{
class OptionsBase
{
[Option('p', "projectid", Required = true, HelpText = "Your Google project id.")]
public string ProjectId { get; set; }
};
[Verb("delete", HelpText = "Delete alert policy")]
class DeleteAlertPolicyOptions
{
[Option('m', "policyname", HelpText = "The name of the policy to delete.", Required = true)]
public string PolicyName { get; set; }
}
[Verb("delete-channel", HelpText = "Delete alert policy")]
class DeleteNotificationChannelOptions
{
[Option('m', "channelname", HelpText = "The name of the channel to delete.", Required = true)]
public string ChannelName { get; set; }
[Option('f', "force", Required = false)]
public bool Force { get; set; } = false;
}
[Verb("list", HelpText = "List alert policies.")]
class ListPoliciesOptions : OptionsBase { };
[Verb("list-channels", HelpText = "List notification channels.")]
class ListChannelsOptions : OptionsBase { };
[Verb("backup", HelpText = "Save the current list of alert policies to a .json file.")]
class BackupPoliciesOptions : OptionsBase
{
[Option('j', "jsonPath", HelpText = "Path to json file where alert polices are saved and restored.")]
public string OutputFilePath { get; set; } = "policies-backup.json";
};
[Verb("restore", HelpText = "Restore the list of alert policies from a .json file.")]
class RestorePoliciesOptions : BackupPoliciesOptions { };
[Verb("replace-channels", HelpText = "Set the list of channel for an alert policy.")]
class ReplaceChannelsOptions : OptionsBase
{
[Option('a', "alertid", Required = true,
HelpText = "The id of the alert policy whose channels will be replaced.")]
public string AlertId { get; set; }
[Option('c', "channelid", Required = true,
HelpText = "A channel id. Repeat this option to set multiple channel ids.")]
public IEnumerable<string> ChannelId { get; set; }
};
[Verb("enable", HelpText = "Enable alert policies.")]
class EnablePoliciesOptions : OptionsBase
{
[Option('e', "filter",
HelpText = "See https://cloud.google.com/monitoring/api/v3/filters")]
public string Filter { get; set; } = "";
};
[Verb("disable", HelpText = "Disable alert policies.")]
class DisablePoliciesOptions : EnablePoliciesOptions { };
public class AlertProgram
{
public static int Main(string[] args)
{
var verbMap = new VerbMap<int>()
.Add((ListPoliciesOptions opts) => ListAlertPolicies(opts.ProjectId))
.Add((BackupPoliciesOptions opts) => BackupPolicies(opts.ProjectId, opts.OutputFilePath))
.Add((RestorePoliciesOptions opts) => RestorePolicies(opts.ProjectId, opts.OutputFilePath))
.Add((ReplaceChannelsOptions opts) => ReplaceChannels(opts.ProjectId, opts.AlertId, opts.ChannelId))
.Add((EnablePoliciesOptions opts) => EnablePolicies(opts.ProjectId, opts.Filter, true))
.Add((DisablePoliciesOptions opts) => EnablePolicies(opts.ProjectId, opts.Filter, false))
.Add((DeleteAlertPolicyOptions opts) => DeleteAlertPolicy(opts.PolicyName))
.Add((ListChannelsOptions opts) => ListNotificationChannels(opts.ProjectId))
.Add((DeleteNotificationChannelOptions opts) => DeleteNotificationChannel(opts.ChannelName, opts.Force))
.SetNotParsedFunc((errs) => 1);
return verbMap.Run(args);
}
// [START monitoring_alert_list_policies]
static void ListAlertPolicies(string projectId)
{
var client = AlertPolicyServiceClient.Create();
var response = client.ListAlertPolicies(new ProjectName(projectId));
foreach (AlertPolicy policy in response)
{
Console.WriteLine(policy.Name);
if (policy.DisplayName != null)
{
Console.WriteLine(policy.DisplayName);
}
if (policy.Documentation?.Content != null)
{
Console.WriteLine(policy.Documentation.Content);
}
Console.WriteLine();
}
}
// [END monitoring_alert_list_policies]
// [START monitoring_alert_list_channels]
static void ListNotificationChannels(string projectId)
{
var client = NotificationChannelServiceClient.Create();
var response = client.ListNotificationChannels(new ProjectName(projectId));
foreach (NotificationChannel channel in response)
{
Console.WriteLine(channel.Name);
if (channel.DisplayName != null)
{
Console.WriteLine(channel.DisplayName);
}
Console.WriteLine();
}
}
// [END monitoring_alert_list_channels]
// [START monitoring_alert_backup_policies]
static void BackupPolicies(string projectId, string filePath)
{
var policyClient = AlertPolicyServiceClient.Create();
var channelClient = NotificationChannelServiceClient.Create();
var projectName = new ProjectName(projectId);
File.WriteAllText(filePath, JsonConvert.SerializeObject(
new BackupRecord()
{
ProjectId = projectId,
Policies = policyClient.ListAlertPolicies(projectName),
Channels = channelClient.ListNotificationChannels(projectName)
}, new ProtoMessageConverter()));
}
// [END monitoring_alert_backup_policies]
// [START monitoring_alert_restore_policies]
// [START monitoring_alert_create_policy]
// [START monitoring_alert_create_channel]
// [START monitoring_alert_update_channel]
static void RestorePolicies(string projectId, string filePath)
{
var policyClient = AlertPolicyServiceClient.Create();
var channelClient = NotificationChannelServiceClient.Create();
List<Exception> exceptions = new List<Exception>();
var backup = JsonConvert.DeserializeObject<BackupRecord>(
File.ReadAllText(filePath), new ProtoMessageConverter());
var projectName = new ProjectName(projectId);
bool isSameProject = projectId == backup.ProjectId;
// When a channel is recreated, rather than updated, it will get
// a new name. We have to update the AlertPolicy with the new
// name. Track the names in this map.
var channelNameMap = new Dictionary<string, string>();
foreach (NotificationChannel channel in backup.Channels)
{
// [END monitoring_alert_create_policy]
try
{
bool updated = false;
Console.WriteLine("Updating channel.\n{0}",
channel.DisplayName);
// This field is immutable and it is illegal to specify a
// non-default value (UNVERIFIED or VERIFIED) in the
// Create() or Update() operations.
channel.VerificationStatus = NotificationChannel.Types
.VerificationStatus.Unspecified;
if (isSameProject)
try
{
channelClient.UpdateNotificationChannel(
null, channel);
updated = true;
}
catch (Grpc.Core.RpcException e)
when (e.Status.StatusCode == StatusCode.NotFound)
{ }
if (!updated)
{
// The channel no longer exists. Recreate it.
string oldName = channel.Name;
channel.Name = null;
var response = channelClient.CreateNotificationChannel(
projectName, channel);
channelNameMap.Add(oldName, response.Name);
}
}
catch (Exception e)
{
// If one failed, continue trying to update the others.
exceptions.Add(e);
}
// [START monitoring_alert_create_policy]
}
foreach (AlertPolicy policy in backup.Policies)
{
// [END monitoring_alert_create_channel]
// [END monitoring_alert_update_channel]
string policyName = policy.Name;
// These two fields cannot be set directly, so clear them.
policy.CreationRecord = null;
policy.MutationRecord = null;
// Update channel names if the channel was recreated with
// another name.
for (int i = 0; i < policy.NotificationChannels.Count; ++i)
{
if (channelNameMap.ContainsKey(policy.NotificationChannels[i]))
{
policy.NotificationChannels[i] =
channelNameMap[policy.NotificationChannels[i]];
}
}
try
{
Console.WriteLine("Updating policy.\n{0}",
policy.DisplayName);
bool updated = false;
if (isSameProject)
try
{
policyClient.UpdateAlertPolicy(null, policy);
updated = true;
}
catch (Grpc.Core.RpcException e)
when (e.Status.StatusCode == StatusCode.NotFound)
{ }
if (!updated)
{
// The policy no longer exists. Recreate it.
policy.Name = null;
foreach (var condition in policy.Conditions)
{
condition.Name = null;
}
policyClient.CreateAlertPolicy(projectName, policy);
}
Console.WriteLine("Restored {0}.", policyName);
}
catch (Exception e)
{
// If one failed, continue trying to update the others.
exceptions.Add(e);
}
// [START monitoring_alert_create_channel]
// [START monitoring_alert_update_channel]
}
if (exceptions.Count > 0)
{
throw new AggregateException(exceptions);
}
}
// [END monitoring_alert_create_policy]
// [END monitoring_alert_create_channel]
// [END monitoring_alert_update_channel]
// [START monitoring_alert_backup_policies]
class BackupRecord
{
public string ProjectId { get; set; }
public IEnumerable<AlertPolicy> Policies { get; set; }
public IEnumerable<NotificationChannel> Channels { get; set; }
}
/// <summary>
/// Lets Newtonsoft.Json and Protobuf's json converters play nicely
/// together. The default Netwtonsoft.Json Deserialize method will
/// not correctly deserialize proto messages.
/// </summary>
class ProtoMessageConverter : JsonConverter
{
public override bool CanConvert(System.Type objectType)
{
return typeof(Google.Protobuf.IMessage)
.IsAssignableFrom(objectType);
}
public override object ReadJson(JsonReader reader,
System.Type objectType, object existingValue,
JsonSerializer serializer)
{
// Read an entire object from the reader.
var converter = new ExpandoObjectConverter();
object o = converter.ReadJson(reader, objectType, existingValue,
serializer);
// Convert it back to json text.
string text = JsonConvert.SerializeObject(o);
// And let protobuf's parser parse the text.
IMessage message = (IMessage)Activator
.CreateInstance(objectType);
return Google.Protobuf.JsonParser.Default.Parse(text,
message.Descriptor);
}
public override void WriteJson(JsonWriter writer, object value,
JsonSerializer serializer)
{
writer.WriteRawValue(Google.Protobuf.JsonFormatter.Default
.Format((IMessage)value));
}
}
// [END monitoring_alert_restore_policies]
// [END monitoring_alert_backup_policies]
// [START monitoring_alert_replace_channels]
static void ReplaceChannels(string projectId, string alertPolicyId,
IEnumerable<string> channelIds)
{
var alertClient = AlertPolicyServiceClient.Create();
var policy = new AlertPolicy()
{
Name = new AlertPolicyName(projectId, alertPolicyId).ToString()
};
foreach (string channelId in channelIds)
{
policy.NotificationChannels.Add(
new NotificationChannelName(projectId, channelId)
.ToString());
}
var response = alertClient.UpdateAlertPolicy(
new FieldMask { Paths = { "notification_channels" } }, policy);
Console.WriteLine("Updated {0}.", response.Name);
}
// [END monitoring_alert_replace_channels]
// [START monitoring_alert_disable_policies]
// [START monitoring_alert_enable_policies]
static object EnablePolicies(string projectId, string filter, bool enable)
{
var client = AlertPolicyServiceClient.Create();
var request = new ListAlertPoliciesRequest()
{
ProjectName = new ProjectName(projectId),
Filter = filter
};
var response = client.ListAlertPolicies(request);
int result = 0;
foreach (AlertPolicy policy in response)
{
try
{
if (policy.Enabled == enable)
{
Console.WriteLine("Policy {0} is already {1}.",
policy.Name, enable ? "enabled" : "disabled");
continue;
}
policy.Enabled = enable;
var fieldMask = new FieldMask { Paths = { "enabled" } };
client.UpdateAlertPolicy(fieldMask, policy);
Console.WriteLine("{0} {1}.", enable ? "Enabled" : "Disabled",
policy.Name);
}
catch (Grpc.Core.RpcException e)
when (e.Status.StatusCode == StatusCode.InvalidArgument)
{
Console.WriteLine(e.Message);
result -= 1;
}
}
// Return a negative count of how many enable operations failed.
return result;
}
// [END monitoring_alert_enable_policies]
// [END monitoring_alert_disable_policies]
static void DeleteAlertPolicy(string policyNameString)
{
var client = AlertPolicyServiceClient.Create();
AlertPolicyName policyName;
if (!AlertPolicyName.TryParse(policyNameString, out policyName))
{
string message = string.Format(
@"{0} is not a valid alert policy name.
Policy names look like this: {1}.",
policyNameString,
new AlertPolicyName("project-id", "alert-policy-id"));
throw new Exception(message);
}
client.DeleteAlertPolicy(policyName);
Console.WriteLine($"Deleted {policyName}.");
}
static void DeleteNotificationChannel(string channelNameString,
bool force)
{
var client = NotificationChannelServiceClient.Create();
NotificationChannelName channelName;
if (!NotificationChannelName.TryParse(channelNameString,
out channelName))
{
string message = string.Format(
@"{0} is not a valid notification channel name.
Channel names look like this: {1}.",
channelNameString,
new NotificationChannelName("project-id",
"notification-channel-id"));
throw new Exception(message);
}
client.DeleteNotificationChannel(channelName, force);
Console.WriteLine($"Deleted {channelName}.");
}
}
}