Repository: Azure/WALinuxAgent
Pull request #728 (2017-05-22 23:25:54)

Fix issues #727, #725, and #721
[#727] -- v2.2.11 cannot update status for RDFE VMs
[#725] -- v2.2.11 can throw unexpected exceptions on provisioned VMs
[#721] -- WALA deprovision process not clean up all cloudinit files

Signed-off-by: Brendan Dixon <brendandixon@me.com>


Patch:
diff --git a/azurelinuxagent/common/osutil/factory.py b/azurelinuxagent/common/osutil/factory.py
index 34476514..df3a006f 100644
--- a/azurelinuxagent/common/osutil/factory.py
+++ b/azurelinuxagent/common/osutil/factory.py
@@ -29,7 +29,6 @@ from .suse import SUSEOSUtil, SUSE11OSUtil
 from .ubuntu import UbuntuOSUtil, Ubuntu12OSUtil, Ubuntu14OSUtil, UbuntuSnappyOSUtil
 from .alpine import AlpineOSUtil
 from .bigip import BigIpOSUtil
-from .gaia import GaiaOSUtil
 
 
 def get_osutil(distro_name=DISTRO_NAME,
@@ -90,9 +89,6 @@ def get_osutil(distro_name=DISTRO_NAME,
     elif distro_name == "bigip":
         return BigIpOSUtil()
 
-    elif distro_name == "gaia":
-        return GaiaOSUtil()
-
     else:
         logger.warn("Unable to load distro implementation for {0}. Using "
                     "default distro implementation instead.",
diff --git a/azurelinuxagent/common/osutil/gaia.py b/azurelinuxagent/common/osutil/gaia.py
deleted file mode 100644
index a1069d33..00000000
--- a/azurelinuxagent/common/osutil/gaia.py
+++ /dev/null
@@ -1,151 +0,0 @@
-#
-# Copyright 2017 Check Point Software Technologies
-#
-# 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.
-#
-# Requires Python 2.4+ and Openssl 1.0+
-#
-
-import socket
-import struct
-import time
-
-import azurelinuxagent.common.logger as logger
-from azurelinuxagent.common.exception import OSUtilError
-import azurelinuxagent.common.utils.shellutil as shellutil
-import azurelinuxagent.common.utils.textutil as textutil
-from azurelinuxagent.common.osutil.default import DefaultOSUtil
-
-
-class GaiaOSUtil(DefaultOSUtil):
-    def __init__(self):
-        super(GaiaOSUtil, self).__init__()
-
-    def _run_clish(self, cmd, log_cmd=True):
-        for i in xrange(10):
-            ret, out = shellutil.run_get_output(
-                "/bin/clish -s -c '" + cmd + "'", log_cmd=log_cmd)
-            if not ret:
-                break
-            if 'NMSHST0025' in out:  # Entry for [hostname] already present
-                ret = 0
-                break
-            time.sleep(2)
-        return ret, out
-
-    def useradd(self, username, expiration=None):
-        logger.warn('useradd is not supported on GAiA')
-
-    def chpasswd(self, username, password, crypt_id=6, salt_len=10):
-        logger.info('chpasswd')
-        passwd_hash = textutil.gen_password_hash(password, crypt_id, salt_len)
-        ret, out = self._run_clish(
-            'set user admin password-hash ' + passwd_hash, log_cmd=False)
-        if ret != 0:
-            raise OSUtilError(("Failed to set password for {0}: {1}"
-                               "").format('admin', out))
-
-    def conf_sudoer(self, username, nopasswd=False, remove=False):
-        logger.info('conf_sudoer is not supported on GAiA')
-
-    def del_root_password(self):
-        logger.info('del_root_password')
-        ret, out = self._run_clish('set user admin password-hash *LOCK*')
-        if ret != 0:
-            raise OSUtilError("Failed to delete root password")
-
-    def _replace_user(path, username):
-        parts = path.split('/')
-        for i in xrange(len(parts)):
-            if parts[i] == '$HOME':
-                parts[i + 1] = username
-                break
-        return '/'.join(parts)
-
-    def deploy_ssh_keypair(self, username, keypair):
-        logger.info('deploy_ssh_keypair')
-        username = 'admin'
-        path, thumbprint = keypair
-        path = self._replace_user(path, username)
-        super(GaiaOSUtil, self).deploy_ssh_keypair(
-            username, (path, thumbprint))
-
-    def deploy_ssh_pubkey(self, username, pubkey):
-        logger.info('deploy_ssh_pubkey')
-        username = 'admin'
-        path, thumbprint, value = pubkey
-        path = self._replace_user(path, username)
-        super(GaiaOSUtil, self).deploy_ssh_pubkey(
-            'admin', (path, thumbprint, value))
-
-    def eject_dvd(self, chk_err=True):
-        logger.warn('eject is not supported on GAiA')
-
-    def mount(self, dvd, mount_point, option="", chk_err=True):
-        logger.info('mount {0} {1} {2}', dvd, mount_point, option)
-        if 'udf,iso9660' in option:
-            ret, out = super(GaiaOSUtil, self).mount(
-                dvd, mount_point, option=option.replace('udf,iso9660', 'udf'),
-                chk_err=chk_err)
-            if not ret:
-                return ret, out
-        return super(GaiaOSUtil, self).mount(
-            dvd, mount_point, option=option, chk_err=chk_err)
-
-    def allow_dhcp_broadcast(self):
-        logger.info('allow_dhcp_broadcast is ignored on GAiA')
-
-    def remove_rules_files(self, rules_files=''):
-        pass
-
-    def restore_rules_files(self, rules_files=''):
-        logger.info('restore_rules_files is ignored on GAiA')
-
-    def restart_ssh_service(self):
-        return shellutil.run('/sbin/service sshd condrestart', chk_err=False)
-
-    def _address_to_string(addr):
-        return socket.inet_ntoa(struct.pack("!I", addr))
-
-    def _get_prefix(self, mask):
-        return str(sum([bin(int(x)).count('1') for x in mask.split('.')]))
-
-    def route_add(self, net, mask, gateway):
-        logger.info('route_add {0} {1} {2}', net, mask, gateway)
-
-        if net == 0 and mask == 0:
-            cidr = 'default'
-        else:
-            cidr = self._address_to_string(net) + '/' + self._get_prefix(
-                self._address_to_string(mask))
-
-        ret, out = self._run_clish(
-            'set static-route ' + cidr +
-            ' nexthop gateway address ' +
-            self._address_to_string(gateway) + ' on')
-        return ret
-
-    def set_hostname(self, hostname):
-        logger.warn('set_hostname is ignored on GAiA')
-
-    def set_dhcp_hostname(self, hostname):
-        logger.warn('set_dhcp_hostname is ignored on GAiA')
-
-    def publish_hostname(self, hostname):
-        logger.warn('publish_hostname is ignored on GAiA')
-
-    def del_account(self, username):
-        logger.warn('del_account is ignored on GAiA')
-
-    def set_admin_access_to_ip(self, dest_ip):
-        logger.warn('set_admin_access_to_ip is ignored on GAiA')
diff --git a/azurelinuxagent/common/protocol/wire.py b/azurelinuxagent/common/protocol/wire.py
index f94c6926..936be8c6 100644
--- a/azurelinuxagent/common/protocol/wire.py
+++ b/azurelinuxagent/common/protocol/wire.py
@@ -830,10 +830,9 @@ class WireClient(object):
         if blob_uri is not None:
 
             if not blob_type in ["BlockBlob", "PageBlob"]:
-                self.report_status_event(
-                    "{0} is an unsupported blob type",
-                    blob_type)
-                return
+                blob_type = "BlockBlob"
+                logger.info("Status Blob type is unspecified "
+                    "-- assuming it is a BlockBlob")
 
             try:
                 self.status_blob.prepare(blob_type)
diff --git a/azurelinuxagent/common/version.py b/azurelinuxagent/common/version.py
index f4324ccc..890a9af5 100644
--- a/azurelinuxagent/common/version.py
+++ b/azurelinuxagent/common/version.py
@@ -57,22 +57,6 @@ def get_f5_platform():
     return result
 
 
-def get_checkpoint_platform():
-    take = build = release = ""
-    full_name = open("/etc/cp-release").read().strip()
-    with open("/etc/cloud-version") as f:
-        for line in f:
-            k, _, v = line.partition(": ")
-            v = v.strip()
-            if k == "release":
-                release = v
-            elif k == "take":
-                take = v
-            elif k == "build":
-                build = v
-    return ["gaia", take + "." + build, release, full_name]
-
-
 def get_distro():
     if 'FreeBSD' in platform.system():
         release = re.sub('\-.*\Z', '', ustr(platform.release()))
@@ -100,9 +84,6 @@ def get_distro():
     if os.path.exists("/shared/vadc"):
         osinfo = get_f5_platform()
 
-    if os.path.exists("/etc/cp-release"):
-        osinfo = get_checkpoint_platform()
-
     # Remove trailing whitespace and quote in distro name
     osinfo[0] = osinfo[0].strip('"').strip(' ').lower()
     return osinfo
diff --git a/azurelinuxagent/pa/deprovision/default.py b/azurelinuxagent/pa/deprovision/default.py
index 35634365..657c19bf 100644
--- a/azurelinuxagent/pa/deprovision/default.py
+++ b/azurelinuxagent/pa/deprovision/default.py
@@ -17,6 +17,7 @@
 # Requires Python 2.4+ and Openssl 1.0+
 #
 
+import glob
 import os.path
 import signal
 import sys
@@ -99,20 +100,60 @@ class DeprovisionHandler(object):
         dirs_to_del = [conf.get_lib_dir()]
         actions.append(DeprovisionAction(fileutil.rm_dirs, dirs_to_del))
 
-    def cloud_init_directories(self):
-        return ["/var/lib/cloud/instance",
-            "/var/lib/cloud/instances/",
-            "/var/lib/cloud/data"]
+    def del_lib_dir_files(self, warnings, actions):
+        known_files = [
+            'HostingEnvironmentConfig.xml',
+            'Incarnation',
+            'Protocol',
+            'SharedConfig.xml',
+            'WireServerEndpoint'
+        ]
+        known_files_glob = [
+            'Extensions.*.xml',
+            'ExtensionsConfig.*.xml',
+            'GoalState.*.xml'
+        ]
+
+        lib_dir = conf.get_lib_dir()
+        files = [f for f in \
+                    [os.path.join(lib_dir, kf) for kf in known_files] \
+                        if os.path.isfile(f)]
+        for p in known_files_glob:
+            files += glob.glob(os.path.join(lib_dir, p))
 
-    def cloud_init_files(self):
-        return ["/etc/sudoers.d/90-cloud-init-users"]
+        if len(files) > 0:
+            actions.append(DeprovisionAction(fileutil.rm_files, files))
 
-    def del_cloud_init(self, warnings, actions):
-        dirs = [d for d in self.cloud_init_directories() if os.path.isdir(d)]
+    def cloud_init_dirs(self, include_once=True):
+        dirs = [
+            "/var/lib/cloud/instance",
+            "/var/lib/cloud/instances/",
+            "/var/lib/cloud/data"
+        ]
+        if include_once:
+            dirs += [
+                "/var/lib/cloud/scripts/per-once"
+            ]
+        return dirs
+    
+    def cloud_init_files(self, include_once=True):
+        files = [
+            "/etc/sudoers.d/90-cloud-init-users"
+        ]
+        if include_once:
+            files += [
+                "/var/lib/cloud/sem/config_scripts_per_once.once"
+            ]
+        return files
+
+    def del_cloud_init(self, warnings, actions, include_once=True):
+        dirs = [d for d in self.cloud_init_dirs(include_once=include_once) \
+                    if os.path.isdir(d)]
         if len(dirs) > 0:
             actions.append(DeprovisionAction(fileutil.rm_dirs, dirs))
 
-        files = [f for f in self.cloud_init_files() if os.path.isfile(f)]
+        files = [f for f in self.cloud_init_files(include_once=include_once) \
+                    if os.path.isfile(f)]
         if len(files) > 0:
             actions.append(DeprovisionAction(fileutil.rm_files, files))
 
@@ -147,19 +188,55 @@ class DeprovisionHandler(object):
 
         return warnings, actions
 
+    def setup_changed_unique_id(self):
+        warnings = []
+        actions = []
+
+        self.del_cloud_init(warnings, actions, include_once=False)
+        self.del_dhcp_lease(warnings, actions)
+        self.del_lib_dir_files(warnings, actions)
+        self.del_resolv(warnings, actions)
+
+        return warnings, actions
+
     def run(self, force=False, deluser=False):
         warnings, actions = self.setup(deluser)
-        for warning in warnings:
-            print(warning)
 
-        if not force:
-            confirm = read_input("Do you want to proceed (y/n)")
-            if not confirm.lower().startswith('y'):
-                return
+        self.do_warnings(warnings)
+        self.do_confirmation(force=force)
+        self.do_actions(actions)
+
+    def run_changed_unique_id(self):
+        '''
+        Clean-up files and directories that may interfere when the VM unique
+        identifier has changed.
+
+        While users *should* manually deprovision a VM, the files removed by
+        this routine will help keep the agent from getting confused
+        (since incarnation and extension settings, among other items, will 
+        no longer be monotonically increasing).
+        '''
+        warnings, actions = self.setup_changed_unique_id()
+
+        self.do_warnings(warnings)
+        self.do_actions(actions)
 
+    def do_actions(self, actions):
         self.actions_running = True
         for action in actions:
             action.invoke()
+        self.actions_running = False
+
+    def do_confirmation(self, force=False):
+        if force:
+            return True
+
+        confirm = read_input("Do you want to proceed (y/n)")
+        return True if confirm.lower().startswith('y') else False
+    
+    def do_warnings(self, warnings):
+        for warning in warnings:
+            print(warning)
 
     def handle_interrupt_signal(self, signum, frame):
         if not self.actions_running:
diff --git a/azurelinuxagent/pa/provision/default.py b/azurelinuxagent/pa/provision/default.py
index f6492be8..d4870f1f 100644
--- a/azurelinuxagent/pa/provision/default.py
+++ b/azurelinuxagent/pa/provision/default.py
@@ -143,15 +143,35 @@ class ProvisionHandler(object):
         return os.path.join(conf.get_lib_dir(), PROVISIONED_FILE)
 
     def is_provisioned(self):
+        '''
+        A VM is considered provisionend *anytime* the provisioning
+        sentinel file exists and not provisioned *anytime* the file
+        is absent.
+
+        If the VM was provisioned using an agent that did not record
+        the VM unique identifier, the provisioning file will be re-written
+        to include the identifier.
+
+        A warning is logged *if* the VM unique identifier has changed
+        since VM was provisioned.
+        '''
         if not os.path.isfile(self.provisioned_file_path()):
             return False
 
-        s = fileutil.read_file(self.provisioned_file_path())
-        util = get_osutil()
-        if s == util.get_instance_id():
-            return True
+        s = fileutil.read_file(self.provisioned_file_path()).strip()
+        if s != self.osutil.get_instance_id():
+            if len(s) > 0:
+                logger.warn("VM is provisioned, "
+                            "but the VM unique identifier has changed -- "
+                            "clearing cached state")
+                from azurelinuxagent.pa.deprovision \
+                    import get_deprovision_handler
+                deprovision_handler = get_deprovision_handler()
+                deprovision_handler.run_changed_unique_id()
 
-        raise ProtocolError("Provisioning new instance without a deprovision")
+            self.write_provisioned()
+
+        return True
 
     def write_provisioned(self):
         fileutil.write_file(
diff --git a/config/gaia/waagent.conf b/config/gaia/waagent.conf
deleted file mode 100644
index 43ad35d7..00000000
--- a/config/gaia/waagent.conf
+++ /dev/null
@@ -1,106 +0,0 @@
-#
-# Microsoft Azure Linux Agent Configuration
-#
-
-# Enable instance creation
-Provisioning.Enabled=y
-
-# Rely on cloud-init to provision
-Provisioning.UseCloudInit=n
-
-# Password authentication for root account will be unavailable.
-Provisioning.DeleteRootPassword=n
-
-# Generate fresh host key pair.
-Provisioning.RegenerateSshHostKeyPair=n
-
-# Supported values are "rsa", "dsa" and "ecdsa".
-Provisioning.SshHostKeyPairType=rsa
-
-# Monitor host name changes and publish changes via DHCP requests.
-Provisioning.MonitorHostName=n
-
-# Decode CustomData from Base64.
-Provisioning.DecodeCustomData=y
-
-# Execute CustomData after provisioning.
-Provisioning.ExecuteCustomData=n
-
-# Algorithm used by crypt when generating password hash.
-Provisioning.PasswordCryptId=1
-
-# Length of random salt used when generating password hash.
-#Provisioning.PasswordCryptSaltLength=10
-
-# Allow reset password of sys user
-Provisioning.AllowResetSysUser=y
-
-# Format if unformatted. If 'n', resource disk will not be mounted.
-ResourceDisk.Format=y
-
-# File system on the resource disk
-# Typically ext3 or ext4. FreeBSD images should use 'ufs2' here.
-ResourceDisk.Filesystem=ext3
-
-# Mount point for the resource disk
-ResourceDisk.MountPoint=/mnt/resource
-
-# Create and use swapfile on resource disk.
-ResourceDisk.EnableSwap=y
-
-# Size of the swapfile.
-ResourceDisk.SwapSizeMB=1024
-
-# Comma-seperated list of mount options. See man(8) for valid options.
-ResourceDisk.MountOptions=None
-
-# Enable verbose logging (y|n)
-Logs.Verbose=y
-
-# Is FIPS enabled
-OS.EnableFIPS=n
-
-# Root device timeout in seconds.
-OS.RootDeviceScsiTimeout=300
-
-# If "None", the system default version is used.
-OS.OpensslPath=/var/lib/waagent/openssl
-
-# Set the path to SSH keys and configuration files
-OS.SshDir=/etc/ssh
-
-# If set, agent will use proxy server to access internet
-#HttpProxy.Host=None
-#HttpProxy.Port=None
-
-# Detect Scvmm environment, default is n
-# DetectScvmmEnv=n
-
-#
-# Lib.Dir=/var/lib/waagent
-
-#
-# DVD.MountPoint=/mnt/cdrom/secure
-
-#
-# Pid.File=/var/run/waagent.pid
-
-#
-# Extension.LogDir=/var/log/azure
-
-#
-# Home.Dir=/home
-
-# Enable RDMA management and set up, should only be used in HPC images
-OS.EnableRDMA=n
-
-# Enable or disable goal state processing auto-update, default is enabled
-AutoUpdate.Enabled=n
-
-# Determine the update family, this should not be changed
-# AutoUpdate.GAFamily=Prod
-
-# Determine if the overprovisioning feature is enabled. If yes, hold extension
-# handling until inVMArtifactsProfile.OnHold is false.
-# Default is disabled
-# EnableOverProvisioning=n
diff --git a/init/gaia/waagent b/init/gaia/waagent
deleted file mode 100755
index c706b857..00000000
--- a/init/gaia/waagent
+++ /dev/null
@@ -1,56 +0,0 @@
-#!/bin/bash
-#
-# Init file for AzureLinuxAgent.
-#
-# chkconfig: 2345 60 80
-# description: AzureLinuxAgent
-#
-
-# source function library
-. /etc/rc.d/init.d/functions
-
-RETVAL=0
-FriendlyName="AzureLinuxAgent"
-WAZD_BIN=/usr/sbin/waagent.sh
-
-start()
-{
-    echo -n $"Starting $FriendlyName: "
-    $WAZD_BIN -start &
-    success
-    echo
-}
-
-stop()
-{
-    echo -n $"Stopping $FriendlyName: "
-    killproc -p /var/run/waagent.pid $WAZD_BIN
-    RETVAL=$?
-    echo
-    return $RETVAL
-}
-
-case "$1" in
-    start)
-        start
-        ;;
-    stop)
-        stop
-        ;;
-    restart)
-        stop
-        start
-        ;;
-    reload)
-        ;;
-    report)
-        ;;
-    status)
-        status $WAZD_BIN
-        RETVAL=$?
-        ;;
-    *)
-        echo $"Usage: $0 {start|stop|restart|status}"
-        RETVAL=1
-esac
-exit $RETVAL


Test patch:
diff --git a/tests/pa/test_deprovision.py b/tests/pa/test_deprovision.py
index c53fa4aa..c4cd9b41 100644
--- a/tests/pa/test_deprovision.py
+++ b/tests/pa/test_deprovision.py
@@ -25,12 +25,28 @@ from tests.tools import *
 
 
 class TestDeprovision(AgentTestCase):
+    @patch("azurelinuxagent.pa.deprovision.default.DeprovisionHandler.cloud_init_dirs")
+    @patch("azurelinuxagent.pa.deprovision.default.DeprovisionHandler.cloud_init_files")
+    def test_del_cloud_init_without_once(self,
+                mock_files,
+                mock_dirs):
+        deprovision_handler = get_deprovision_handler("","","")
+        deprovision_handler.del_cloud_init([], [], include_once=False)
+
+        mock_dirs.assert_called_with(include_once=False)
+        mock_files.assert_called_with(include_once=False)
+
     @patch("signal.signal")
     @patch("azurelinuxagent.common.protocol.get_protocol_util")
     @patch("azurelinuxagent.common.osutil.get_osutil")
-    @patch("azurelinuxagent.pa.deprovision.default.DeprovisionHandler.cloud_init_directories")
+    @patch("azurelinuxagent.pa.deprovision.default.DeprovisionHandler.cloud_init_dirs")
     @patch("azurelinuxagent.pa.deprovision.default.DeprovisionHandler.cloud_init_files")
-    def test_del_cloud_init(self, mock_files, mock_dirs, mock_osutil, mock_util, mock_signal):
+    def test_del_cloud_init(self,
+                mock_files,
+                mock_dirs,
+                mock_osutil,
+                mock_util,
+                mock_signal):
         try:
             with tempfile.NamedTemporaryFile() as f:
                 warnings = []
@@ -45,6 +61,9 @@ class TestDeprovision(AgentTestCase):
                 deprovision_handler = get_deprovision_handler("","","")
                 deprovision_handler.del_cloud_init(warnings, actions)
 
+                mock_dirs.assert_called_with(include_once=True)
+                mock_files.assert_called_with(include_once=True)
+
                 self.assertEqual(len(warnings), 0)
                 self.assertEqual(len(actions), 2)
                 for da in actions:
@@ -65,6 +84,47 @@ class TestDeprovision(AgentTestCase):
         except OSError:
             # Ignore the error caused by removing the file within the "with"
             pass
+    
+    @distros("ubuntu")
+    @patch('azurelinuxagent.common.conf.get_lib_dir')
+    def test_del_lib_dir_files(self,
+                        distro_name,
+                        distro_version,
+                        distro_full_name,
+                        mock_conf):
+        files = [
+            'HostingEnvironmentConfig.xml',
+            'Incarnation',
+            'Protocol',
+            'SharedConfig.xml',
+            'WireServerEndpoint',
+            'Extensions.1.xml',
+            'ExtensionsConfig.1.xml',
+            'GoalState.1.xml',
+            'Extensions.2.xml',
+            'ExtensionsConfig.2.xml',
+            'GoalState.2.xml'
+        ]
+
+        tmp = tempfile.mkdtemp()
+        mock_conf.return_value = tmp
+        for f in files:
+            fileutil.write_file(os.path.join(tmp, f), "Value")
+
+        deprovision_handler = get_deprovision_handler(distro_name,
+                                                      distro_version,
+                                                      distro_full_name)
+        warnings = []
+        actions = []
+        deprovision_handler.del_lib_dir_files(warnings, actions)
+
+        self.assertTrue(len(warnings) == 0)
+        self.assertTrue(len(actions) == 1)
+        self.assertEqual(fileutil.rm_files, actions[0].func)
+        self.assertTrue(len(actions[0].args) > 0)
+        for f in actions[0].args:
+            self.assertTrue(os.path.basename(f) in files)
+
 
     @distros("redhat")
     def test_deprovision(self,
diff --git a/tests/pa/test_provision.py b/tests/pa/test_provision.py
index 72038959..04464424 100644
--- a/tests/pa/test_provision.py
+++ b/tests/pa/test_provision.py
@@ -61,16 +61,37 @@ class TestProvision(AgentTestCase):
     @patch('os.path.isfile', return_value=True)
     @patch('azurelinuxagent.common.utils.fileutil.read_file',
             return_value="B9F3C233-9913-9F42-8EB3-BA656DF32502")
-    def test_is_provisioned_is_provisioned(self, mock_read, mock_isfile):
+    @patch('azurelinuxagent.pa.deprovision.get_deprovision_handler')
+    def test_is_provisioned_is_provisioned(self,
+            mock_deprovision, mock_read, mock_isfile):
         ph = ProvisionHandler()
+        ph.osutil = Mock()
+        ph.osutil.get_instance_id = \
+            Mock(return_value="B9F3C233-9913-9F42-8EB3-BA656DF32502")
+        ph.write_provisioned = Mock()
+
+        deprovision_handler = Mock()
+        mock_deprovision.return_value = deprovision_handler
+
         self.assertTrue(ph.is_provisioned())
+        deprovision_handler.run_changed_unique_id.assert_not_called()
 
     @patch('os.path.isfile', return_value=True)
     @patch('azurelinuxagent.common.utils.fileutil.read_file',
-            side_effect=["Value1", "Value2"])
-    def test_is_provisioned_not_deprovisioned(self, mock_read, mock_isfile):
+            side_effect=["Value"])
+    @patch('azurelinuxagent.pa.deprovision.get_deprovision_handler')
+    def test_is_provisioned_not_deprovisioned(self,
+            mock_deprovision, mock_read, mock_isfile):
+
         ph = ProvisionHandler()
-        self.assertRaises(ProtocolError, ph.is_provisioned)
+        ph.osutil = Mock()
+        ph.write_provisioned = Mock()
+
+        deprovision_handler = Mock()
+        mock_deprovision.return_value = deprovision_handler
+
+        self.assertTrue(ph.is_provisioned())
+        deprovision_handler.run_changed_unique_id.assert_called_once()
 
 if __name__ == '__main__':
     unittest.main()
diff --git a/tests/protocol/test_wire.py b/tests/protocol/test_wire.py
index 016dc348..ba9fc7db 100644
--- a/tests/protocol/test_wire.py
+++ b/tests/protocol/test_wire.py
@@ -191,19 +191,23 @@ class TestWireProtocolGetters(AgentTestCase):
                     self.assertTrue(HostPluginProtocol.is_default_channel())
                     HostPluginProtocol.set_default_channel(False)
 
-    def test_upload_status_blob_reports_type_error(self, *args):
+    def test_upload_status_blob_unknown_type_assumes_block(self, *args):
         vmstatus = VMStatus(message="Ready", status="Ready")
         wire_protocol_client = WireProtocol(wireserver_url).client
         wire_protocol_client.ext_conf = ExtensionsConfig(None)
         wire_protocol_client.ext_conf.status_upload_blob = testurl
         wire_protocol_client.ext_conf.status_upload_blob_type = "NotALegalType"
         wire_protocol_client.status_blob.vm_status = vmstatus
-        goal_state = GoalState(WireProtocolData(DATA_FILE).goal_state)
 
-        with patch.object(WireClient, "report_status_event") as mock_event:
-            wire_protocol_client.upload_status_blob()
+        with patch.object(WireClient, "get_goal_state") as patch_get_goal_state:
+            with patch.object(StatusBlob, "prepare") as patch_prepare:
+                with patch.object(StatusBlob, "upload") as patch_default_upload:
+                    HostPluginProtocol.set_default_channel(False)
+                    wire_protocol_client.upload_status_blob()
 
-            self.assertEqual(mock_event.call_count, 1)
+                    patch_prepare.assert_called_once_with("BlockBlob")
+                    patch_default_upload.assert_called_once_with(testurl)
+                    patch_get_goal_state.assert_not_called()
 
     def test_upload_status_blob_reports_prepare_error(self, *args):
         vmstatus = VMStatus(message="Ready", status="Ready")