> This patch has basic support for users and groups on Windows.
>
> * It for version 0.24.7.
> * It does not include support for active directory.
> * It does not work with the 0.24.8 beta.

Cool, can I just say this is really awesome!

But you really should target master for new features like this.

Also rather than a monolithic patch could you post individual patches, see

http://reductivelabs.com/trac/puppet/wiki/DevelopmentLifecycle

> The code is up at http://github.com/finalprefix/puppet/tree/0.24.7-win.
>
> I'd love any feedback you might have!

How are you finding running it on windows, there are a bunch of path
issues I know that need to be cleaned up to run puppet.

If I get a chance this week I'll try and produce a combined tree
cherry-picking your stuff against master.

Further comments inline

> diff --git a/lib/puppet.rb b/lib/puppet.rb
> index 4b0091c..77dbdfe 100644
> --- a/lib/puppet.rb
> +++ b/lib/puppet.rb
> @@ -190,53 +190,57 @@ module Puppet
>     # Trap a couple of the main signals.  This should probably be
> handled
> +       begin
> +               [:INT, :TERM].each do |signal|
> +                   trap(signal) do
> +                       Puppet.notice "Caught #{signal}; shutting down"
> +                       Puppet.debug "Signal caught here:"
> +                       caller.each { |l| Puppet.debug l }
> +                       Puppet.shutdown
> +                   end
> +               end
...
> +        rescue ArgumentError => e
> +            raise if e.to_s.index('SIGHUP') == nil
>         end
>     end

What's the issue you're fixing here, I'm assuming its due to SIGHUP
not being defined on windows. There is probably a more explicit way of
handling this, as it's such a long method it makes it hard to see the
intent.

> diff --git a/lib/puppet/provider/group/groupadd_win.rb b/lib/puppet/
> provider/group/groupadd_win.rb
> new file mode 100644
> index 0000000..c1fa7f4
> --- /dev/null
> +++ b/lib/puppet/provider/group/groupadd_win.rb
> @@ -0,0 +1,30 @@
> +require 'puppet/util/windows_system'
> +
> +Puppet::Type.type(:group).provide :groupadd_win do
> +       desc "Group management for windows"
> +
> +       defaultfor :operatingsystem => :windows
> +
> +       has_features :manages_members
> +
> +       def members
> +               Windows::Group.new(@resource[:name]).members
> +       end
> +
> +       def members=(members)
> +               Windows::Group.new(@resource[:name]).set_members(members)
> +       end
> +
> +       def create
> +               group = Windows::Group.create(@resource[:name])
> +               group.set_members(@resource[:members])
> +       end
> +
> +       def exists?
> +               Windows::Group.exists?(@resource[:name])
> +       end
> +
> +       def delete
> +               Windows::Group.delete(@resource[:name])
> +       end
> +end

Looks quite nice simple set of  providers sitting on top of your utils class

> diff --git a/lib/puppet/util/windows_system.rb b/lib/puppet/util/
> windows_system.rb
> new file mode 100644
> index 0000000..83bc52e
> --- /dev/null
> +++ b/lib/puppet/util/windows_system.rb
> @@ -0,0 +1,205 @@
> +if Facter.operatingsystem == "windows"
> +       require 'win32ole'
> +       require 'Win32API'
> +end
> +
> +module ADSI
> +       def ADSI.connectable?(uri)
> +               begin
> +                       adsi_obj = WIN32OLE.connect(uri)
> +                       return adsi_obj != nil;
> +               rescue
> +               end
> +
> +               return false
> +       end
> +end
> +
> +module Windows
> +       class Resource
> +               def Resource.uri(resource_name)
> +                       "#{Computer.resource_uri}/#{resource_name}"
> +               end
> +       end
> +
> +       class User
> +               def initialize(username, native_adsi_obj = nil)
> +                       @username = username
> +                       @user = native_adsi_obj
> +               end
> +
> +               def user
> +                       @user = 
> WIN32OLE.connect(User.resource_uri(@username)) if @user ==
> nil
> +                       return @user
> +               end
> +
> +               def password_is?(password)
> +                       fLOGON32_LOGON_NETWORK_CLEARTEXT = 8
> +                       fLOGON32_PROVIDER_DEFAULT = 0
> +
> +                       logon_user = Win32API.new("advapi32", "LogonUser", 
> ['P', 'P', 'P',
> 'L', 'L', 'P'], 'L')
> +                       close_handle = Win32API.new("kernel32", 
> "CloseHandle", ['P'], 'V')
> +
> +                       token = ' ' * 4
> +                       if logon_user.call(@username, "", password,
> fLOGON32_LOGON_NETWORK_CLEARTEXT, fLOGON32_PROVIDER_DEFAULT, token) ==
> 1
> +                               close_handle.call(token.unpack('L')[0])
> +                               return true
> +                       end
> +
> +                       return false
> +               end
> +
> +               def add_flag(flag_name, value)
> +                       flag = 0
> +
> +                       begin
> +                               flag = user.Get(flag_name)
> +                       rescue
> +                       end
> +
> +                       user.Put(flag_name, flag | value)
> +                       user.SetInfo
> +               end
> +
> +               def password=(password)
> +                       user.SetPassword(password)
> +                       user.SetInfo
> +
> +                       fADS_UF_DONT_EXPIRE_PASSWD = 0x10000
> +                       add_flag("UserFlags", fADS_UF_DONT_EXPIRE_PASSWD)
> +               end
> +
> +               def groups
> +                       groups = []
> +                       user.Groups.each {|group| groups << group.name }
> +                       return groups
> +               end
> +
> +               def add_to_groups(group_names)
> +                       group_names.each {|name| 
> Group.new(name).add_user(@username) } if
> group_names.length > 0
> +               end
> +
> +               def remove_from_groups(group_names)
> +                       group_names.each {|name| 
> Group.new(name).remove_user(@username) }
> if group_names.length > 0
> +               end
> +
> +               def set_groups(names, minimal = true)
> +                       return if names == nil || names.strip.length == 0
> +
> +                       names = names.strip.split(',')
> +                       current_groups = groups
> +
> +                       names.find_all {|name| !current_groups.include?(name) 
> }.tap {|
> names_to_add| add_to_groups(names_to_add) }
> +                       current_groups.find_all {|name| !names.include?(name) 
> }.tap {|
> names_to_remove| remove_from_groups(names_to_remove) } if minimal ==
> false
> +               end
> +
> +               def User.resource_uri(username)
> +                       return "#{Resource.uri(username)},user"
> +               end
> +
> +               def User.exists?(username)
> +                       return ADSI::connectable?(User.resource_uri(username))
> +               end
> +
> +               def User.create(username, password)
> +                       User.new(username, Computer.create("user", 
> username)).tap {|user|
> user.password = password; yield user if block_given? }
> +               end
> +
> +               def User.delete(username)
> +                       Computer.delete("user", username)
> +               end
> +       end
> +
> +       class Group
> +               def initialize(groupname, native_adsi_obj = nil)
> +                       @groupname = groupname
> +                       @group = native_adsi_obj
> +               end
> +
> +               def resource_uri
> +                       Group.resource_uri(@groupname)
> +               end
> +
> +               def Group.resource_uri(name)
> +                       "#{Resource.uri(name)},group"
> +               end
> +
> +               def group
> +                       @group = WIN32OLE.connect(resource_uri) if @group == 
> nil
> +                       return @group
> +               end
> +
> +               def add_user(username)
> +                       group.Add(User.resource_uri(username))
> +                       group.SetInfo
> +               end
> +
> +               def remove_user(username)
> +                       group.Remove(User.resource_uri(username))
> +                       group.SetInfo
> +               end
> +
> +               def add_member(name)
> +                       group.Add(Resource.uri(name))
> +                       group.SetInfo
> +               end
> +
> +               def remove_member(name)
> +                       group.Remove(Resource.uri(name))
> +                       group.SetInfo
> +               end
> +
> +               def members
> +                       list = []
> +                       group.Members.each {|member| list << member.Name }
> +                       list
> +               end
> +
> +               def set_members(members)
> +                       return nil if members == nil || members.length == 0
> +
> +                       current_members = self.members
> +
> +                       members.inject([]) {|members_to_add, member|
> current_members.include?(member) ? members_to_add : members_to_add <<
> member }.each {|member| add_member(member) }
> +                       current_members.inject([]) {|members_to_remove, 
> member|
> members.include?(member) ? members_to_remove : members_to_remove <<
> member }.each {|member| remove_member(member) }
> +               end
> +
> +               def Group.create(name)
> +                       Windows::Group.new(name, Computer.create("group", 
> name)).tap {|
> group| yield group if block_given? }
> +               end
> +
> +               def Group.exists?(name)
> +                       return ADSI::connectable?(Group.resource_uri(name))
> +               end
> +
> +               def Group.delete(name)
> +                       Computer.delete("group", name)
> +               end
> +       end
> +
> +       class Computer
> +               def Computer.name
> +                       name = " " * 128
> +                       size = "128"
> +                       
> Win32API.new('kernel32','GetComputerName',['P','P'],'I').call
> (name,size)
> +                       return name.unpack("A*")
> +               end
> +
> +               def Computer.resource_uri
> +                       computer_name = Computer.name
> +                       return "WinNT://#{computer_name}"
> +               end
> +
> +               def Computer.api
> +                       return WIN32OLE.connect(Computer.resource_uri)
> +               end
> +
> +               def Computer.create(resource_type, name)
> +                       Computer.api.create(resource_type, name).SetInfo
> +               end
> +
> +               def Computer.delete(resource_type, name)
> +                       Computer.api.Delete(resource_type, name)
> +               end
> +       end
> +end

Looks good at initial pass, I'll need to setup a windows box again in
order to test.

> diff --git a/test/ral/providers/group_win.rb b/test/ral/providers/

OK we're moving to rspec for testing all new functionality should be
tested using rspec rather than Test::Unit, although tests appreciated.

If you need a hand with this I'm often on irc during UTC working hours
(nick nasrat)

> diff --git a/test/ral/providers/syslog.rb b/test/ral/providers/
> syslog.rb
> new file mode 100644
> index 0000000..396b45c
> --- /dev/null
> +++ b/test/ral/providers/syslog.rb
> @@ -0,0 +1,4 @@
> +# The purpose of this file is merely to fake the syslog module on
> windows.
> +# I placed it here since I was running the tests from here.
> +# I'll probably leave the file here until I wrap the windows event
> log in a syslogish module for windows.
> +# I haven't thought about it much. I'm open to better ideas too.

I have an actual fix for not forcing loading syslog on windows, so we
can just force the log type in config for now.

Again thanks

Paul

--~--~---------~--~----~------------~-------~--~----~
You received this message because you are subscribed to the Google Groups 
"Puppet Developers" group.
To post to this group, send email to [email protected]
To unsubscribe from this group, send email to 
[email protected]
For more options, visit this group at 
http://groups.google.com/group/puppet-dev?hl=en
-~----------~----~----~----~------~----~------~--~---

Reply via email to